#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
主窗口界面
样本管理系统的主界面
"""

import sys
import os
import logging
from datetime import datetime
from typing import Optional
from pathlib import Path

from PyQt6.QtWidgets import (
    QMainWindow, QWidget, QVBoxLayout, QHBoxLayout, QGridLayout,
    QPushButton, QLabel, QLineEdit, QTextEdit, QTableWidget,
    QTableWidgetItem, QTabWidget, QGroupBox, QCheckBox,
    QComboBox, QSpinBox, QMessageBox, QFileDialog, QDialog,
    QFormLayout, QGroupBox, QStatusBar, QAbstractItemView,
    QHeaderView, QSplitter, QFrame, QCheckBox, QDoubleSpinBox,
    QTextEdit, QApplication, QScrollArea
)
from PyQt6.QtCore import Qt, QTimer, QDate, QTime
from PyQt6.QtGui import QFont, QIcon, QPixmap

# 使用绝对导入，避免相对导入问题
try:
    from src.core.database_manager import DatabaseManager
    from src.core.communication_manager import CommunicationManager
    from src.core.data_processor import DataProcessor
    from src.core.sample_push_service import SamplePushService
    from src.models.models import User, Sample, OperationLog, UserRole, SampleStatus, CommunicationType, ProtocolType
    from src.ui.dialogs import LoginDialog, AdminSetupDialog, SampleEditDialog, ImageViewerDialog, UserEditDialog, UserPasswordDialog, UserPermissionDialog
    from src.ui.batch_send_dialog import BatchSendDialog
    from src.ui.batch_export_dialog import BatchExportDialog
    from src.utils.logger_manager import get_system_logger, set_system_logger, SystemLoggerManager
    from src.utils.pagination_manager import PaginationManager
    from src.ui.modules.pagination_widget import PaginationWidget
    from src.utils.export_manager import ExportManager
    from src.utils.logger_config import configure_logging_once
except ImportError:
    # 如果绝对导入失败，尝试相对导入
    try:
        from ..core.database_manager import DatabaseManager
        from ..core.communication_manager import CommunicationManager
        from ..core.data_processor import DataProcessor
        from ..core.sample_push_service import SamplePushService
        from ..models.models import User, Sample, OperationLog, UserRole, SampleStatus, CommunicationType, ProtocolType
        from .dialogs import LoginDialog, AdminSetupDialog, SampleEditDialog, ImageViewerDialog, UserEditDialog, UserPasswordDialog, UserPermissionDialog
        from .batch_send_dialog import BatchSendDialog
        from .batch_export_dialog import BatchExportDialog
        from ..utils.logger_manager import get_system_logger, set_system_logger, SystemLoggerManager
        from ..utils.pagination_manager import PaginationManager
        from .modules.pagination_widget import PaginationWidget
        from ..utils.export_manager import ExportManager
        from ..utils.logger_config import configure_logging_once
    except ImportError:
        # 如果都失败，抛出明确的错误
        raise ImportError("无法导入必要的模块，请检查项目结构和依赖")

logger = logging.getLogger(__name__)

class SampleManagementSystem(QMainWindow):
    """样本管理系统主窗口"""
    
    def _check_permission(self, required_role: UserRole):
        """检查用户权限"""
        if not self.current_user:
            QMessageBox.warning(self, "警告", "请先登录")
            return False
        
        # 管理员拥有所有权限
        if self.current_user.role == UserRole.ADMIN:
            return True
        
        # 查看员只能执行查看相关操作
        if required_role == UserRole.VIEWER:
            return True
        
        # 其他情况需要管理员权限
        QMessageBox.warning(self, "权限不足", f"此操作需要管理员权限")
        return False
    
    def __init__(self):
        super().__init__()
        
        # 首先配置日志系统，确保只配置一次
        self._configure_logging()
        
        # 初始化数据库管理器
        self.db_manager = DatabaseManager()
        
        # 初始化系统日志管理器
        self.system_logger = SystemLoggerManager(self.db_manager)
        set_system_logger(self.system_logger)
        
        # 初始化通信管理器
        self.comm_manager = CommunicationManager(self.db_manager)
        self.data_processor = DataProcessor()
        
        # 初始化样本推送服务
        self.sample_push_service = SamplePushService()
        
        self.current_user = None
        
        # 初始化优化的分页管理器
        from src.utils.optimized_pagination_manager import OptimizedPaginationManager, LazyDataLoader
        from src.core.sample_data_service import SampleDataService
        
        # 创建样本数据服务
        self.sample_data_service = SampleDataService(self.db_manager)
        
        # 创建懒加载数据管理器
        self.lazy_data_loader = LazyDataLoader(self.db_manager, cache_size=50)
        
        # 初始化样本分页管理器
        from src.utils.optimized_pagination_manager import SortOrder
        self.sample_pagination_manager = OptimizedPaginationManager(
            page_size=20,
            data_loader=self.lazy_data_loader.load_page,
            sort_field="updated_at",
            sort_order=SortOrder.DESC
        )
        
        # 设置样本数据服务到分页管理器
        self.sample_pagination_manager.set_sample_data_service(self.sample_data_service)
        
        # 初始化日志分页管理器（保持原有实现）
        self.logs_pagination_manager = PaginationManager(page_size=20)
        
        # 初始化导出管理器
        self.export_manager = ExportManager()
        
        # 初始化备份管理器
        self.backup_manager = None
        self.init_backup_manager()
        
        # 检查是否需要初始化
        self.check_initialization()
        
        # 登录
        if not self.login():
            sys.exit()
        
        # 设置当前用户到日志管理器
        if self.current_user:
            self.system_logger.set_current_user(self.current_user.username)
        
        self.init_ui()
        
        # 设置数据回调 - 启用数据接收处理
        self.comm_manager.set_data_callback(self.on_data_received)
        
        # 延迟加载样本数据，确保UI完全初始化后再加载
        QTimer.singleShot(100, self.load_samples)
        
        # 启动TCP服务器（如果启用）
        self.start_tcp_server_if_enabled()
        
        # 启动TCP服务器状态更新定时器
        self.tcp_status_timer = QTimer()
        self.tcp_status_timer.timeout.connect(self.update_tcp_server_status)
        self.tcp_status_timer.start(3000)  # 每3秒更新一次状态
        
        # 初始化刷新定时器和状态标志
        self._refresh_timer = None
        self._is_refreshing = False
        self._batch_transfer_active = False
        self._last_data_received_time = None
        self._batch_transfer_timer = None
        self._batch_transfer_count = 0  # 批量传输样本计数
        
        # 自动刷新功能已删除
        
        # 记录系统启动日志
        self.system_logger.log_system_event(
            "系统启动",
            "样本管理系统已启动",
            details={"user": self.current_user.username if self.current_user else "未知"}
        )
        
        # 根据用户权限更新UI状态
        self.update_ui_permissions()
    
    def _configure_logging(self):
        """配置日志系统"""
        try:
            if configure_logging_once:
                # 使用新的日志配置模块
                configure_logging_once(
                    log_level="INFO",
                    log_file="system.log",
                    console_output=True,
                    file_output=True
                )
            else:
                # 备用方案：基本日志配置
                root_logger = logging.getLogger()
                if not root_logger.handlers:
                    console_handler = logging.StreamHandler()
                    console_handler.setLevel(logging.INFO)
                    formatter = logging.Formatter(
                        '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
                    )
                    console_handler.setFormatter(formatter)
                    root_logger.addHandler(console_handler)
                    root_logger.setLevel(logging.INFO)
        except Exception as e:
            print(f"配置日志系统失败: {e}")
    
    def init_backup_manager(self):
        """初始化备份管理器"""
        try:
            # 使用绝对导入，避免相对导入问题
            try:
                from src.core.backup_manager import BackupManager
            except ImportError:
                try:
                    from ..core.backup_manager import BackupManager
                except ImportError:
                    logger.error("无法导入备份管理器模块")
                    return
            
            # 获取配置文件路径
            config_path = None
            try:
                from src.utils.config import Config
                project_root = Path(__file__).parent.parent.parent
                config_path = str(project_root / "data" / "config.json")
            except Exception as e:
                logger.warning(f"无法获取配置文件路径: {e}")
                # 使用默认路径
                project_root = Path(__file__).parent.parent.parent
                config_path = str(project_root / "data" / "config.json")
            
            # 创建备份管理器
            self.backup_manager = BackupManager(
                db_path=self.db_manager.db_path,
                config_path=config_path
            )
            
            logger.info("备份管理器初始化成功")
            
        except Exception as e:
            logger.error(f"初始化备份管理器失败: {e}")
    
    def update_backup_checkpoint(self):
        """更新备份检查点"""
        try:
            if self.backup_manager:
                self.backup_manager.update_checkpoint()
        except Exception as e:
            logger.error(f"更新备份检查点失败: {e}")
    
    def check_initialization(self):
        """检查系统是否需要初始化"""
        import sqlite3
        with sqlite3.connect(self.db_manager.db_path) as conn:
            cursor = conn.cursor()
            cursor.execute("SELECT COUNT(*) FROM users")
            user_count = cursor.fetchone()[0]
            
            if user_count == 0:
                dialog = AdminSetupDialog(self.db_manager)
                if dialog.exec() != QDialog.DialogCode.Accepted:
                    sys.exit()
    
    def login(self) -> bool:
        """用户登录"""
        dialog = LoginDialog(self.db_manager)
        if dialog.exec() == QDialog.DialogCode.Accepted:
            self.current_user = dialog.current_user
            return True
        return False
    
    def init_ui(self):
        """初始化UI"""
        self.setWindowTitle("小苏科技-样本管理系统")
        
        # 设置窗口最小尺寸，确保在小屏幕上也能正常显示
        self.setMinimumSize(1024, 768)
        
        # 获取屏幕尺寸，设置合适的窗口大小
        screen = QApplication.primaryScreen().geometry()
        window_width = min(1400, screen.width() - 100)
        window_height = min(900, screen.height() - 100)
        self.setGeometry(100, 100, window_width, window_height)
        
        # 创建菜单栏
        self.create_menu_bar()
        
        # 创建中央部件
        central_widget = QWidget()
        self.setCentralWidget(central_widget)
        
        # 主布局
        main_layout = QVBoxLayout(central_widget)
        main_layout.setContentsMargins(5, 5, 5, 5)  # 减少边距
        main_layout.setSpacing(5)  # 减少间距
        
        # 创建标签页
        self.tab_widget = QTabWidget()
        main_layout.addWidget(self.tab_widget)
        
        # 连接标签页切换信号
        self.tab_widget.currentChanged.connect(self.on_tab_changed)
        
        # 根据用户角色添加不同的功能页面
        try:
            # 所有用户都可以访问样本管理和系统日志
            self.init_sample_management_tab()
            self.init_system_logs_tab()
            
            # 只有管理员可以访问用户管理和系统设置
            if self.current_user and self.current_user.role == UserRole.ADMIN:
                self.init_user_management_tab()
                self.init_system_settings_tab()
        except Exception as e:
            logger.error(f"初始化标签页失败: {e}")
        
        # 状态栏
        self.status_bar = QStatusBar()
        self.setStatusBar(self.status_bar)
        if self.current_user:
            self.status_bar.showMessage(f"欢迎，{self.current_user.username} | 角色：{self.current_user.role.value}")
        else:
            self.status_bar.showMessage("欢迎使用LIS样本管理系统")
        
        # 定时器更新状态
        try:
            self.status_timer = QTimer()
            self.status_timer.timeout.connect(self.update_status)
            self.status_timer.start(5000)  # 5秒更新一次
        except Exception as e:
            logger.error(f"启动状态定时器失败: {e}")
        
        # 批量传输状态更新定时器
        try:
            self.batch_status_timer = QTimer()
            self.batch_status_timer.timeout.connect(self.update_batch_transfer_status)
            self.batch_status_timer.start(1000)  # 1秒更新一次（更频繁的检查）
        except Exception as e:
            logger.error(f"启动批量传输状态定时器失败: {e}")
        
        # 连接窗口大小变化事件
        self.resizeEvent = self.on_resize_event
    
    def on_tab_changed(self, index):
        """标签页切换事件处理"""
        try:
            # 获取当前标签页的文本
            current_tab_text = self.tab_widget.tabText(index)
            
            # 如果切换到系统日志标签页，自动刷新日志
            if current_tab_text == "系统日志":
                logger.info("切换到系统日志标签页，自动刷新日志")
                self.load_logs()
                
                # 更新状态栏
                if hasattr(self, 'status_bar'):
                    self.status_bar.showMessage("系统日志已自动刷新")
            
            # 如果切换到样本管理标签页，自动刷新样本数据
            elif current_tab_text == "样本管理":
                logger.info("切换到样本管理标签页，自动刷新样本数据")
                # 使用标准加载方法
                self.load_samples()
                
                # 更新状态栏
                if hasattr(self, 'status_bar'):
                    self.status_bar.showMessage("样本数据已自动刷新")
                
            # 如果切换到用户管理标签页，自动刷新用户数据
            elif current_tab_text == "用户管理":
                # 检查用户权限
                if self.current_user and self.current_user.role == UserRole.ADMIN:
                    logger.info("切换到用户管理标签页，自动刷新用户数据")
                    self.load_users()
                    
                    # 更新状态栏
                    if hasattr(self, 'status_bar'):
                        self.status_bar.showMessage("用户数据已自动刷新")
                else:
                    logger.warning("非管理员用户尝试访问用户管理页面")
                    QMessageBox.warning(self, "权限不足", "只有管理员可以访问用户管理功能")
                    # 切换回样本管理标签页
                    self.tab_widget.setCurrentIndex(0)
                
            # 如果切换到系统设置标签页，自动刷新系统设置
            elif current_tab_text == "系统设置":
                # 检查用户权限
                if self.current_user and self.current_user.role == UserRole.ADMIN:
                    logger.info("切换到系统设置标签页，自动刷新系统设置")
                    self.load_settings()
                    
                    # 更新状态栏
                    if hasattr(self, 'status_bar'):
                        self.status_bar.showMessage("系统设置已自动刷新")
                else:
                    logger.warning("非管理员用户尝试访问系统设置页面")
                    QMessageBox.warning(self, "权限不足", "只有管理员可以访问系统设置功能")
                    # 切换回样本管理标签页
                    self.tab_widget.setCurrentIndex(0)
                
        except Exception as e:
            logger.error(f"标签页切换事件处理失败: {e}")
            if hasattr(self, 'status_bar'):
                self.status_bar.showMessage(f"标签页切换失败: {e}")
    
    # 自动刷新功能已删除，保留必要的刷新方法
    def manual_refresh_samples(self):
        """手动刷新样本数据"""
        try:
            logger.info("开始手动刷新样本数据...")
            self.load_samples()
            if hasattr(self, 'status_bar'):
                self.status_bar.showMessage("样本数据已手动刷新")
        except Exception as e:
            logger.error(f"手动刷新样本数据失败: {e}")
    
    def refresh_after_new_sample(self):
        """新样本入库后刷新数据 - 添加防抖机制避免无限循环"""
        try:
            # 检查是否已经在刷新中，避免重复刷新
            if hasattr(self, '_is_refreshing') and self._is_refreshing:
                logger.debug("刷新操作正在进行中，跳过本次刷新")
                return
            
            # 检查是否正在进行批量传输，如果是则跳过刷新
            if hasattr(self, '_batch_transfer_active') and self._batch_transfer_active:
                logger.debug(f"正在进行批量传输，跳过刷新避免干扰（累计样本: {self._batch_transfer_count}）")
                return
            
            # 设置刷新状态标志
            self._is_refreshing = True
            
            logger.info("新样本入库后，开始刷新数据...")
            
            # 检查当前是否在样本管理标签页
            if self.tab_widget.currentIndex() == 0:  # 样本管理标签页
                # 获取当前样本总数作为比较基准
                current_total = self.sample_pagination_manager.get_pagination_info().total_items
                logger.info(f"刷新前样本总数: {current_total}")
                
                # 检查是否有搜索条件
                search_text = self.search_edit.text().strip() if hasattr(self, 'search_edit') else ""
                
                if search_text:
                    # 如果有搜索条件，执行搜索刷新
                    logger.info("新样本入库后：执行搜索刷新")
                    self.search_samples()
                else:
                    # 如果没有搜索条件，执行普通刷新
                    logger.info("新样本入库后：执行普通刷新")
                    self.load_samples()
                
                # 获取刷新后的样本总数
                new_total = self.sample_pagination_manager.get_pagination_info().total_items
                logger.info(f"刷新后样本总数: {new_total}")
                
                # 检测数据变化
                data_change = new_total - current_total
                
                # 更新状态栏显示
                if hasattr(self, 'status_bar'):
                    pagination_info = self.sample_pagination_manager.get_pagination_info()
                    
                    if data_change > 0:
                        change_info = f"🆕 发现 {data_change} 个新样本"
                    elif data_change < 0:
                        change_info = f"🗑️ 减少 {abs(data_change)} 个样本"
                    else:
                        change_info = "📊 数据无变化"
                    
                    status_text = f"🔄 新样本入库后数据已刷新 - {change_info} | {pagination_info.total_items} 个样本，第 {pagination_info.current_page} 页 | 刷新时间: {QTime.currentTime().toString('HH:%M:%S')}"
                    self.status_bar.showMessage(status_text)
                
                logger.info(f"新样本入库后数据刷新完成，变化: {data_change}")
                
                # 如果数据没有变化，记录警告并避免继续刷新
                if data_change == 0:
                    logger.warning("数据刷新后无变化，可能存在重复刷新问题")
            else:
                logger.debug("当前不在样本管理标签页，跳过刷新")
                
        except Exception as e:
            logger.error(f"新样本入库后刷新数据失败: {e}")
            if hasattr(self, 'status_bar'):
                self.status_bar.showMessage(f"❌ 新样本入库后刷新失败: {str(e)}")
        finally:
            # 重置刷新状态标志
            self._is_refreshing = False
    
    def force_refresh_samples(self):
        """强制刷新样本数据（立即刷新最新数据）"""
        try:
            logger.info("开始强制刷新样本数据...")
            
            # 检查当前是否在样本管理标签页
            if self.tab_widget.currentIndex() == 0:  # 样本管理标签页
                # 获取当前样本总数作为比较基准
                current_total = self.sample_pagination_manager.get_pagination_info().total_items
                logger.info(f"强制刷新前样本总数: {current_total}")
                
                # 仅在必要时清除缓存，避免过度清理
                if hasattr(self, 'sample_pagination_manager'):
                    # 清除分页管理器缓存
                    self.sample_pagination_manager.clear_cache()
                    logger.info("强制刷新：已清除分页管理器缓存")
                
                # 不强制清除数据加载器缓存，保持性能
                
                # 检查是否有搜索条件
                search_text = self.search_edit.text().strip() if hasattr(self, 'search_edit') else ""
                
                if search_text:
                    # 如果有搜索条件，执行搜索刷新
                    logger.info("强制刷新：执行搜索刷新")
                    self.search_samples()
                else:
                    # 如果没有搜索条件，执行普通刷新
                    logger.info("强制刷新：执行普通刷新")
                    self.load_samples()
                
                # 获取刷新后的样本总数
                new_total = self.sample_pagination_manager.get_pagination_info().total_items
                logger.info(f"强制刷新后样本总数: {new_total}")
                
                # 更新状态栏显示
                if hasattr(self, 'status_bar'):
                    pagination_info = self.sample_pagination_manager.get_pagination_info()
                    
                    # 检测数据变化
                    if new_total > current_total:
                        change_info = f"🆕 发现 {new_total - current_total} 个新样本"
                    elif new_total < current_total:
                        change_info = f"🗑️ 减少 {current_total - new_total} 个样本"
                    else:
                        change_info = "📊 数据无变化"
                    
                    status_text = f"⚡ 强制刷新完成 - {change_info} | {pagination_info.total_items} 个样本，第 {pagination_info.current_page} 页 | 刷新时间: {QTime.currentTime().toString('HH:mm:ss')}"
                    self.status_bar.showMessage(status_text)
                
                logger.info(f"强制刷新完成，共 {pagination_info.total_items} 个样本，变化: {new_total - current_total}")
                
                # 显示成功提示
                QMessageBox.information(self, "刷新完成", f"数据刷新完成！\n\n当前样本总数: {pagination_info.total_items}\n数据变化: {change_info}")
                
            else:
                QMessageBox.warning(self, "警告", "当前不在样本管理标签页")
                
        except Exception as e:
            logger.error(f"强制刷新样本数据失败: {e}")
            QMessageBox.critical(self, "错误", f"强制刷新失败: {str(e)}")
            if hasattr(self, 'status_bar'):
                self.status_bar.showMessage(f"❌ 强制刷新失败: {str(e)}")
    
    def on_tcp_reconnect_success(self):
        """TCP重连成功处理"""
        try:
            logger.info("收到TCP重连成功回调，开始重建应用层协议...")
            
            # 更新状态栏显示
            if hasattr(self, 'status_bar'):
                self.status_bar.showMessage("🔄 TCP重连成功，正在重建应用层协议...")
            
            # 延迟执行，确保连接完全建立
            QTimer.singleShot(2000, self._rebuild_tcp_session_after_reconnect)
            
            logger.info("TCP重连成功处理完成")
            
        except Exception as e:
            logger.error(f"处理TCP重连成功失败: {e}")
            if hasattr(self, 'status_bar'):
                self.status_bar.showMessage(f"❌ TCP重连成功处理失败: {str(e)}")
    
    def _rebuild_tcp_session_after_reconnect(self):
        """TCP重连后重建应用层会话"""
        try:
            logger.info("开始重建TCP应用层会话...")
            
            # 更新状态栏显示
            if hasattr(self, 'status_bar'):
                self.status_bar.showMessage("🔄 正在重建TCP应用层会话...")
            
            # 发送应用层握手包
            self._send_tcp_handshake()
            
            # 发送订阅请求
            self._send_tcp_subscription()
            
            # 发送游标同步请求
            self._send_tcp_cursor_sync()
            
            # 更新状态栏显示
            if hasattr(self, 'status_bar'):
                self.status_bar.showMessage("✅ TCP重连成功，连接已恢复，应用层协议已重建")
            
            logger.info("TCP应用层会话重建完成")
            
        except Exception as e:
            logger.error(f"重建TCP应用层会话失败: {e}")
            if hasattr(self, 'status_bar'):
                self.status_bar.showMessage(f"❌ TCP应用层会话重建失败: {str(e)}")
    
    def _send_tcp_handshake(self):
        """发送TCP握手包"""
        try:
            if hasattr(self, 'comm_manager') and self.comm_manager.is_connected:
                # 这里可以添加具体的握手逻辑
                logger.info("已发送TCP握手包")
        except Exception as e:
            logger.error(f"发送TCP握手包失败: {e}")
    
    def _send_tcp_subscription(self):
        """发送TCP订阅请求"""
        try:
            if hasattr(self, 'comm_manager') and self.comm_manager.is_connected:
                # 这里可以添加具体的订阅逻辑
                logger.info("已发送TCP订阅请求")
        except Exception as e:
            logger.error(f"发送TCP订阅请求失败: {e}")
    
    def _send_tcp_cursor_sync(self):
        """发送TCP游标同步请求"""
        try:
            if hasattr(self, 'comm_manager') and self.comm_manager.is_connected:
                # 这里可以添加具体的游标同步逻辑
                logger.info("已发送TCP游标同步请求")
        except Exception as e:
            logger.error(f"发送TCP游标同步请求失败: {e}")
    
    def on_resize_event(self, event):
        """窗口大小变化事件处理"""
        try:
            # 调用父类的resizeEvent
            super().resizeEvent(event)
            
            # 调整分割器比例
            if hasattr(self, 'tab_widget') and self.tab_widget.currentWidget():
                current_tab = self.tab_widget.currentWidget()
                
                # 查找分割器
                for child in current_tab.findChildren(QSplitter):
                    if isinstance(child, QSplitter):
                        # 根据窗口宽度调整分割器比例
                        window_width = self.width()
                        if window_width < 1200:
                            # 小屏幕：左侧占更多空间
                            child.setSizes([int(window_width * 0.7), int(window_width * 0.3)])
                        else:
                            # 大屏幕：保持默认比例
                            child.setSizes([600, 400])
                        break
            
            # 调整表格列宽
            self.adjust_table_columns()
            
        except Exception as e:
            logger.error(f"窗口大小变化事件处理失败: {e}")
    
    def adjust_table_columns(self):
        """调整表格列宽以适应窗口大小"""
        try:
            window_width = self.width()
            
            # 调整样本表格列宽
            if hasattr(self, 'sample_table'):
                if window_width < 1200:
                    # 小屏幕：减小列宽
                    self.sample_table.setColumnWidth(11, 100)  # 百分比结果
                    self.sample_table.setColumnWidth(12, 100)  # TIME数据
                    self.sample_table.setColumnWidth(13, 100)  # AREA数据
                else:
                    # 大屏幕：恢复默认列宽
                    self.sample_table.setColumnWidth(11, 150)  # 百分比结果
                    self.sample_table.setColumnWidth(12, 150)  # TIME数据
                    self.sample_table.setColumnWidth(13, 150)  # AREA数据
            
            # 调整日志表格列宽
            if hasattr(self, 'logs_table'):
                if window_width < 1200:
                    # 小屏幕：减小列宽
                    self.logs_table.setColumnWidth(1, 100)  # 操作时间
                    self.logs_table.setColumnWidth(2, 60)   # 操作员
                    self.logs_table.setColumnWidth(3, 60)   # 操作类型
                else:
                    # 大屏幕：恢复默认列宽
                    self.logs_table.setColumnWidth(1, 120)  # 操作时间
                    self.logs_table.setColumnWidth(2, 80)   # 操作员
                    self.logs_table.setColumnWidth(3, 80)   # 操作类型
                    
        except Exception as e:
            logger.error(f"调整表格列宽失败: {e}")
    
    def create_menu_bar(self):
        """创建菜单栏"""
        menubar = self.menuBar()
        
        # 文件菜单
        file_menu = menubar.addMenu("文件(&F)")
        
        file_menu.addSeparator()
        
        # 退出
        exit_action = file_menu.addAction("退出(&X)")
        exit_action.triggered.connect(self.close)
        
        # 工具菜单
        tools_menu = menubar.addMenu("工具(&T)")
        
        # 强制刷新数据
        force_refresh_action = tools_menu.addAction("强制刷新数据")
        force_refresh_action.triggered.connect(self.force_refresh_samples)
        
        # 根据用户角色显示不同的工具菜单
        if self.current_user and self.current_user.role == UserRole.ADMIN:
            tools_menu.addSeparator()
            
            # 备份管理
            backup_action = tools_menu.addAction("备份管理(&B)")
            backup_action.triggered.connect(self.open_backup_manager)
        
        # 帮助菜单
        help_menu = menubar.addMenu("帮助(&H)")
        
        # 关于
        about_action = help_menu.addAction("关于(&A)")
        about_action.triggered.connect(self.show_about)
    
    def init_sample_management_tab(self):
        """初始化样本管理标签页"""
        sample_widget = QWidget()
        layout = QVBoxLayout(sample_widget)
        layout.setContentsMargins(5, 5, 5, 5)  # 减少边距
        layout.setSpacing(5)  # 减少间距
        
        # 工具栏 - 使用垂直布局包装两行按钮
        toolbar_container = QWidget()
        toolbar_container.setMaximumHeight(70)  # 限制工具栏高度
        toolbar_layout = QVBoxLayout(toolbar_container)
        toolbar_layout.setContentsMargins(5, 5, 5, 5)
        toolbar_layout.setSpacing(5)
        
        # 第一行：主要操作按钮
        first_row = QHBoxLayout()
        first_row.setSpacing(5)
        
        # 立即刷新按钮（强制刷新最新数据）
        self.force_refresh_button = QPushButton("立即刷新")
        self.force_refresh_button.clicked.connect(self.force_refresh_samples)
        self.force_refresh_button.setMinimumWidth(80)
        self.force_refresh_button.setStyleSheet("QPushButton { background-color: #2196F3; color: white; }")
        first_row.addWidget(self.force_refresh_button)

        # 根据用户角色显示不同的操作按钮
        if self.current_user and self.current_user.role == UserRole.ADMIN:
            # 管理员可以看到所有操作按钮
            self.add_sample_button = QPushButton("新增样本")
            self.add_sample_button.clicked.connect(self.add_sample)
            self.add_sample_button.setMinimumWidth(80)
            first_row.addWidget(self.add_sample_button)
            
            self.edit_sample_button = QPushButton("编辑样本")
            self.edit_sample_button.clicked.connect(self.edit_sample)
            self.edit_sample_button.setMinimumWidth(80)
            first_row.addWidget(self.edit_sample_button)
            
            self.edit_patient_button = QPushButton("编辑患者信息")
            self.edit_patient_button.clicked.connect(self.edit_patient_info)
            self.edit_patient_button.setMinimumWidth(100)
            first_row.addWidget(self.edit_patient_button)
            
            self.send_button = QPushButton("TCP批量推送")
            self.send_button.clicked.connect(self.send_samples)
            self.send_button.setMinimumWidth(80)
            first_row.addWidget(self.send_button)
            
            self.export_button = QPushButton("批量导出")
            self.export_button.clicked.connect(self.batch_export_samples)
            self.export_button.setMinimumWidth(100)
            first_row.addWidget(self.export_button)
        else:
            # 查看员只能看到查看相关的按钮
            self.view_sample_button = QPushButton("查看样本详情")
            self.view_sample_button.clicked.connect(self.view_sample_details)
            self.view_sample_button.setMinimumWidth(100)
            first_row.addWidget(self.view_sample_button)
        
        first_row.addStretch()
        
        # 第二行：多选操作和搜索
        second_row = QHBoxLayout()
        
        # 根据用户角色显示不同的多选操作按钮
        if self.current_user and self.current_user.role == UserRole.ADMIN:
            # 管理员可以看到所有多选操作按钮
            self.select_all_button = QPushButton("全选")
            self.select_all_button.clicked.connect(self.select_all_samples)
            self.select_all_button.setMinimumWidth(60)
            second_row.addWidget(self.select_all_button)
            
            self.clear_selection_button = QPushButton("清除选择")
            self.clear_selection_button.clicked.connect(self.clear_sample_selection)
            self.clear_selection_button.setMinimumWidth(80)
            second_row.addWidget(self.clear_selection_button)
        else:
            # 查看员只能看到基础的选择操作
            self.clear_selection_button = QPushButton("清除选择")
            self.clear_selection_button.clicked.connect(self.clear_sample_selection)
            self.clear_selection_button.setMinimumWidth(80)
            second_row.addWidget(self.clear_selection_button)
        
        # self.invert_selection_button = QPushButton("反选")
        # self.invert_selection_button.clicked.connect(self.invert_sample_selection)
        # self.invert_selection_button.setMinimumWidth(60)
        # second_row.addWidget(self.invert_selection_button)
        
        second_row.addStretch()
        
        # 搜索区域
        search_layout = QHBoxLayout()
        
        self.search_edit = QLineEdit()
        self.search_edit.setPlaceholderText("搜索样本编号、患者姓名...")
        self.search_edit.textChanged.connect(self.search_samples)
        self.search_edit.setMinimumWidth(200)
        search_layout.addWidget(self.search_edit)
        
        # 高级搜索按钮
        self.advanced_search_button = QPushButton("高级搜索")
        self.advanced_search_button.clicked.connect(self.open_advanced_search)
        self.advanced_search_button.setMinimumWidth(80)
        search_layout.addWidget(self.advanced_search_button)
        
        # 选中样本数量显示
        search_layout.addStretch()
        search_layout.addWidget(QLabel("已选择:"))
        self.selected_count_label = QLabel("0")
        self.selected_count_label.setStyleSheet("color: blue; font-weight: bold;")
        search_layout.addWidget(self.selected_count_label)
        search_layout.addWidget(QLabel("个样本"))
        
        second_row.addLayout(search_layout)
        
        # 添加两行到工具栏容器
        toolbar_layout.addLayout(first_row)
        toolbar_layout.addLayout(second_row)
        
        layout.addWidget(toolbar_container)
        
        # 创建分割器
        splitter = QSplitter(Qt.Orientation.Horizontal)
        splitter.setChildrenCollapsible(False)  # 防止子部件被完全折叠
        
        # 左侧：样本表格
        left_widget = QWidget()
        left_layout = QVBoxLayout(left_widget)
        left_layout.setContentsMargins(2, 2, 2, 2)
        
        # 样本表格
        self.sample_table = QTableWidget()
        self.sample_table.setColumnCount(18)  # 增加一列，调整为18列
        self.sample_table.setHorizontalHeaderLabels([
            "ID", "样本编号", "仪器检测编号", "患者姓名", "年龄", "性别", "仪器型号", "仪器编号", "位置", "设备分析时间", 
            "接收时间", "状态", "百分比结果", "TIME数据", "AREA数据", "分析模式", "图片", "协议"
        ])
        
        # 设置表格属性
        self.sample_table.setSelectionBehavior(QAbstractItemView.SelectionBehavior.SelectRows)
        self.sample_table.setAlternatingRowColors(True)
        self.sample_table.horizontalHeader().setStretchLastSection(True)
        self.sample_table.setSelectionMode(QAbstractItemView.SelectionMode.ExtendedSelection)  # 支持多选
        
        # 设置列宽 - 优化小屏幕显示
        header = self.sample_table.horizontalHeader()
        header.setSectionResizeMode(0, QHeaderView.ResizeMode.Fixed)      # ID列 - 完全隐藏
        header.setSectionResizeMode(1, QHeaderView.ResizeMode.ResizeToContents)  # 样本编号
        header.setSectionResizeMode(2, QHeaderView.ResizeMode.ResizeToContents)  # 仪器检测编号
        header.setSectionResizeMode(3, QHeaderView.ResizeMode.ResizeToContents)  # 患者姓名
        header.setSectionResizeMode(4, QHeaderView.ResizeMode.Fixed)      # 年龄
        header.setSectionResizeMode(5, QHeaderView.ResizeMode.Fixed)      # 性别
        header.setSectionResizeMode(6, QHeaderView.ResizeMode.ResizeToContents)  # 仪器型号
        header.setSectionResizeMode(7, QHeaderView.ResizeMode.ResizeToContents)  # 仪器编号
        header.setSectionResizeMode(8, QHeaderView.ResizeMode.Fixed)      # 位置
        header.setSectionResizeMode(9, QHeaderView.ResizeMode.ResizeToContents)  # 设备分析时间
        header.setSectionResizeMode(10, QHeaderView.ResizeMode.ResizeToContents)  # 接收时间
        header.setSectionResizeMode(11, QHeaderView.ResizeMode.Fixed)     # 状态
        header.setSectionResizeMode(12, QHeaderView.ResizeMode.Interactive)   # 百分比结果 - 可交互调整
        header.setSectionResizeMode(13, QHeaderView.ResizeMode.Interactive)   # TIME数据 - 可交互调整
        header.setSectionResizeMode(14, QHeaderView.ResizeMode.Interactive)   # AREA数据 - 可交互调整
        header.setSectionResizeMode(15, QHeaderView.ResizeMode.ResizeToContents) # 分析模式
        header.setSectionResizeMode(16, QHeaderView.ResizeMode.Fixed)     # 图片
        header.setSectionResizeMode(17, QHeaderView.ResizeMode.ResizeToContents) # 协议
        
        # 完全隐藏ID列
        self.sample_table.hideColumn(0)
        
        # 设置最小列宽，确保在小屏幕上也能正常显示
        self.sample_table.setColumnWidth(0, 0)    # ID列 - 隐藏
        self.sample_table.setColumnWidth(1, 80)   # 样本编号
        self.sample_table.setColumnWidth(2, 80)   # 仪器检测编号
        self.sample_table.setColumnWidth(3, 80)   # 患者姓名
        self.sample_table.setColumnWidth(4, 40)   # 年龄
        self.sample_table.setColumnWidth(5, 40)   # 性别
        self.sample_table.setColumnWidth(6, 80)   # 仪器型号
        self.sample_table.setColumnWidth(7, 80)   # 仪器编号
        self.sample_table.setColumnWidth(8, 40)   # 位置
        self.sample_table.setColumnWidth(9, 120)  # 设备分析时间
        self.sample_table.setColumnWidth(10, 120)  # 接收时间
        self.sample_table.setColumnWidth(11, 60)  # 状态
        self.sample_table.setColumnWidth(12, 150) # 百分比结果 - 减小初始宽度
        self.sample_table.setColumnWidth(13, 150) # TIME数据 - 减小初始宽度
        self.sample_table.setColumnWidth(14, 150) # AREA数据 - 减小初始宽度
        self.sample_table.setColumnWidth(15, 80)  # 分析模式
        self.sample_table.setColumnWidth(16, 60)  # 图片
        self.sample_table.setColumnWidth(17, 80)  # 协议
        
        # 连接选择变化信号
        self.sample_table.itemSelectionChanged.connect(self.on_sample_selection_changed)
        
        # 连接双击信号，用于查看完整内容
        self.sample_table.itemDoubleClicked.connect(self.on_item_double_clicked)
        
        left_layout.addWidget(self.sample_table)
        
        # 添加分页控件到左侧样本列表底部
        self.sample_pagination_widget = PaginationWidget()
        self.sample_pagination_widget.set_pagination_manager(self.sample_pagination_manager)
        self.sample_pagination_widget.page_changed.connect(self.on_sample_page_changed)
        self.sample_pagination_widget.page_size_changed.connect(self.on_sample_page_size_changed)
        left_layout.addWidget(self.sample_pagination_widget)
        
        # 右侧：样本详情预览
        right_widget = QWidget()
        right_layout = QVBoxLayout(right_widget)
        right_layout.setContentsMargins(2, 2, 2, 2)
        
        # 详情标题
        detail_title = QLabel("样本详情预览")
        detail_title.setFont(QFont("Arial", 12, QFont.Weight.Bold))
        detail_title.setAlignment(Qt.AlignmentFlag.AlignCenter)
        detail_title.setMaximumHeight(30)
        right_layout.addWidget(detail_title)
        
        # 创建详情内容的滚动区域
        detail_scroll_area = QScrollArea()
        detail_scroll_area.setWidgetResizable(True)
        detail_scroll_area.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAsNeeded)
        detail_scroll_area.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAsNeeded)
        
        detail_scroll = QWidget()
        detail_scroll_layout = QVBoxLayout(detail_scroll)
        detail_scroll_layout.setContentsMargins(5, 5, 5, 5)
        
        # 基本信息组
        basic_info_group = QGroupBox("基本信息")
        basic_info_layout = QFormLayout(basic_info_group)
        
        self.preview_sample_number = QLabel("")
        self.preview_sample_uid = QLabel("")    # 仪器检测编号
        self.preview_patient_name = QLabel("")  # 患者姓名
        self.preview_patient_age = QLabel("")   # 患者年龄
        self.preview_patient_gender = QLabel("") # 患者性别
        self.preview_position = QLabel("")  # 样本位置
        self.preview_sample_type = QLabel("")  # 样本类型
        self.preview_analysis_time = QLabel("")
        self.preview_receive_time = QLabel("")  # 接收时间
        self.preview_status = QLabel("")
        
        basic_info_layout.addRow("样本编号:", self.preview_sample_number)
        basic_info_layout.addRow("仪器检测编号:", self.preview_sample_uid)
        basic_info_layout.addRow("患者姓名:", self.preview_patient_name)
        basic_info_layout.addRow("患者年龄:", self.preview_patient_age)
        basic_info_layout.addRow("患者性别:", self.preview_patient_gender)
        basic_info_layout.addRow("样本位置:", self.preview_position)
        basic_info_layout.addRow("样本类型:", self.preview_sample_type)
        basic_info_layout.addRow("设备分析时间:", self.preview_analysis_time)
        basic_info_layout.addRow("接收时间:", self.preview_receive_time)
        basic_info_layout.addRow("状态:", self.preview_status)
        
        detail_scroll_layout.addWidget(basic_info_group)
        
        # 仪器信息组
        instrument_info_group = QGroupBox("仪器信息")
        instrument_info_layout = QFormLayout(instrument_info_group)
        
        self.preview_instrument_model = QLabel("")
        self.preview_instrument_number = QLabel("")
        self.preview_analysis_mode = QLabel("")  # 新增分析模式字段
        self.preview_protocol_type = QLabel("")
        
        instrument_info_layout.addRow("仪器型号:", self.preview_instrument_model)
        instrument_info_layout.addRow("仪器编号:", self.preview_instrument_number)
        instrument_info_layout.addRow("分析模式:", self.preview_analysis_mode)  # 新增分析模式字段
        instrument_info_layout.addRow("协议类型:", self.preview_protocol_type)
        
        detail_scroll_layout.addWidget(instrument_info_group)
        
        # 百分比结果组
        percentage_results_group = QGroupBox("百分比结果")
        percentage_results_layout = QVBoxLayout(percentage_results_group)
        
        self.preview_percentage_results = QLabel("")
        self.preview_percentage_results.setWordWrap(True)
        self.preview_percentage_results.setMinimumHeight(80)
        self.preview_percentage_results.setStyleSheet("border: 1px solid #ccc; padding: 5px; background-color: #f9f9f9;")
        percentage_results_layout.addWidget(self.preview_percentage_results)
        
        detail_scroll_layout.addWidget(percentage_results_group)
        
        # TIME数据组
        time_data_group = QGroupBox("TIME数据（出峰时间）")
        time_data_layout = QVBoxLayout(time_data_group)
        
        self.preview_time_data = QLabel("")
        self.preview_time_data.setWordWrap(True)
        self.preview_time_data.setMinimumHeight(80)
        self.preview_time_data.setStyleSheet("border: 1px solid #ccc; padding: 5px; background-color: #f0f8ff;")
        time_data_layout.addWidget(self.preview_time_data)
        
        detail_scroll_layout.addWidget(time_data_group)
        
        # AREA数据组
        area_data_group = QGroupBox("AREA数据（峰面积）")
        area_data_layout = QVBoxLayout(area_data_group)
        
        self.preview_area_data = QLabel("")
        self.preview_area_data.setWordWrap(True)
        self.preview_area_data.setMinimumHeight(80)
        self.preview_area_data.setStyleSheet("border: 1px solid #ccc; padding: 5px; background-color: #fff8f0;")
        area_data_layout.addWidget(self.preview_area_data)
        
        detail_scroll_layout.addWidget(area_data_group)
        
        # 图片预览组
        image_preview_group = QGroupBox("图片预览")
        image_preview_layout = QVBoxLayout(image_preview_group)
        
        self.preview_image_label = QLabel("暂无图片")
        self.preview_image_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
        self.preview_image_label.setMinimumHeight(200)
        self.preview_image_label.setStyleSheet("border: 1px solid #ccc; background-color: #f0f0f0;")
        
        image_preview_layout.addWidget(self.preview_image_label)
        
        # 图片操作按钮
        image_button_layout = QHBoxLayout()
        self.view_image_button = QPushButton("查看大图")
        self.view_image_button.clicked.connect(self.view_preview_image)
        self.view_image_button.setEnabled(False)
        image_button_layout.addWidget(self.view_image_button)
        
        self.save_image_button = QPushButton("保存图片")
        self.save_image_button.clicked.connect(self.save_preview_image)
        self.save_image_button.setEnabled(False)
        image_button_layout.addWidget(self.save_image_button)
        
        image_preview_layout.addLayout(image_button_layout)
        
        detail_scroll_layout.addWidget(image_preview_group)
        
        # 添加弹性空间
        detail_scroll_layout.addStretch()
        
        # 将详情内容添加到滚动区域
        detail_scroll_area.setWidget(detail_scroll)
        
        # 将滚动区域添加到右侧布局
        right_layout.addWidget(detail_scroll_area)
        
        # 设置分割器比例
        splitter.addWidget(left_widget)
        splitter.addWidget(right_widget)
        splitter.setSizes([600, 400])  # 设置左右比例
        
        layout.addWidget(splitter)
        
        self.tab_widget.addTab(sample_widget, "样本管理")
    
    def init_system_logs_tab(self):
        """初始化系统日志标签页"""
        logs_widget = QWidget()
        layout = QVBoxLayout(logs_widget)
        layout.setContentsMargins(5, 5, 5, 5)
        layout.setSpacing(5)
        
        # 工具栏
        toolbar = QHBoxLayout()
        
        self.refresh_logs_button = QPushButton("刷新日志")
        self.refresh_logs_button.clicked.connect(self.load_logs)
        self.refresh_logs_button.setMinimumWidth(80)
        toolbar.addWidget(self.refresh_logs_button)
        
        # 删除导出日志按钮
        # self.export_logs_button = QPushButton("导出日志")
        # self.export_logs_button.clicked.connect(self.export_logs)
        # toolbar.addWidget(self.export_logs_button)
        
        self.view_details_button = QPushButton("查看详情")
        self.view_details_button.clicked.connect(self.view_log_details)
        self.view_details_button.setMinimumWidth(80)
        toolbar.addWidget(self.view_details_button)
        
        toolbar.addStretch()
        
        # 搜索区域
        search_layout = QHBoxLayout()
        
        self.log_search_edit = QLineEdit()
        self.log_search_edit.setPlaceholderText("搜索操作员、操作类型...")
        self.log_search_edit.textChanged.connect(self.search_logs)
        self.log_search_edit.setMinimumWidth(200)
        search_layout.addWidget(self.log_search_edit)
        
        toolbar.addLayout(search_layout)
        
        layout.addLayout(toolbar)
        
        # 日志表格
        self.logs_table = QTableWidget()
        self.logs_table.setColumnCount(6)
        self.logs_table.setHorizontalHeaderLabels([
            "序列号", "操作时间", "操作员", "操作类型", "操作内容", "IP地址"
        ])
        
        self.logs_table.setSelectionBehavior(QAbstractItemView.SelectionBehavior.SelectRows)
        self.logs_table.setAlternatingRowColors(True)
        self.logs_table.horizontalHeader().setStretchLastSection(True)
        
        # 设置列宽 - 优化小屏幕显示
        header = self.logs_table.horizontalHeader()
        header.setSectionResizeMode(0, QHeaderView.ResizeMode.Fixed)
        header.setSectionResizeMode(1, QHeaderView.ResizeMode.ResizeToContents)
        header.setSectionResizeMode(2, QHeaderView.ResizeMode.ResizeToContents)
        header.setSectionResizeMode(3, QHeaderView.ResizeMode.ResizeToContents)
        header.setSectionResizeMode(4, QHeaderView.ResizeMode.Stretch)
        header.setSectionResizeMode(5, QHeaderView.ResizeMode.Fixed)
        
        # 设置最小列宽
        self.logs_table.setColumnWidth(0, 50)   # 序列号
        self.logs_table.setColumnWidth(1, 120)  # 操作时间
        self.logs_table.setColumnWidth(2, 80)   # 操作员
        self.logs_table.setColumnWidth(3, 80)   # 操作类型
        self.logs_table.setColumnWidth(5, 80)   # IP地址
        
        # 双击查看详情
        self.logs_table.itemDoubleClicked.connect(self.view_log_details)
        
        layout.addWidget(self.logs_table)
        
        # 添加分页控件
        self.logs_pagination_widget = PaginationWidget()
        self.logs_pagination_widget.set_pagination_manager(self.logs_pagination_manager)
        self.logs_pagination_widget.page_changed.connect(self.on_logs_page_changed)
        self.logs_pagination_widget.page_size_changed.connect(self.on_logs_page_size_changed)
        layout.addWidget(self.logs_pagination_widget)
        
        self.tab_widget.addTab(logs_widget, "系统日志")
        
        # 加载日志
        self.load_logs()
    
    def init_user_management_tab(self):
        """初始化用户管理标签页"""
        user_widget = QWidget()
        layout = QVBoxLayout(user_widget)
        layout.setContentsMargins(5, 5, 5, 5)
        layout.setSpacing(5)
        
        # 工具栏
        toolbar = QHBoxLayout()
        
        self.refresh_users_button = QPushButton("刷新")
        self.refresh_users_button.clicked.connect(self.load_users)
        self.refresh_users_button.setMinimumWidth(60)
        toolbar.addWidget(self.refresh_users_button)
        
        self.add_user_button = QPushButton("新增用户")
        self.add_user_button.clicked.connect(self.add_user)
        self.add_user_button.setMinimumWidth(80)
        toolbar.addWidget(self.add_user_button)
        
        self.edit_user_button = QPushButton("编辑用户")
        self.edit_user_button.clicked.connect(self.edit_user)
        self.edit_user_button.setMinimumWidth(80)
        toolbar.addWidget(self.edit_user_button)
        
        self.delete_user_button = QPushButton("删除用户")
        self.delete_user_button.clicked.connect(self.delete_user)
        self.delete_user_button.setMinimumWidth(80)
        toolbar.addWidget(self.delete_user_button)
        
        self.batch_delete_users_button = QPushButton("批量删除")
        self.batch_delete_users_button.clicked.connect(self.batch_delete_users)
        self.batch_delete_users_button.setMinimumWidth(80)
        toolbar.addWidget(self.batch_delete_users_button)
        
        self.change_password_button = QPushButton("修改密码")
        self.change_password_button.clicked.connect(self.change_user_password)
        self.change_password_button.setMinimumWidth(80)
        toolbar.addWidget(self.change_password_button)
        
        self.permission_button = QPushButton("权限管理")
        self.permission_button.clicked.connect(self.manage_user_permissions)
        self.permission_button.setMinimumWidth(80)
        toolbar.addWidget(self.permission_button)
        
        toolbar.addStretch()
        
        # 搜索区域
        search_layout = QHBoxLayout()
        
        self.user_search_edit = QLineEdit()
        self.user_search_edit.setPlaceholderText("搜索用户名、角色...")
        self.user_search_edit.textChanged.connect(self.search_users)
        self.user_search_edit.setMinimumWidth(200)
        search_layout.addWidget(self.user_search_edit)
        
        toolbar.addLayout(search_layout)
        
        layout.addLayout(toolbar)
        
        # 用户表格
        self.users_table = QTableWidget()
        self.users_table.setColumnCount(6)
        self.users_table.setHorizontalHeaderLabels([
            "选择", "用户ID", "用户名", "角色", "创建时间", "最后登录"
        ])
        
        self.users_table.setSelectionBehavior(QAbstractItemView.SelectionBehavior.SelectRows)
        self.users_table.setAlternatingRowColors(True)
        self.users_table.horizontalHeader().setStretchLastSection(True)
        self.users_table.setSelectionMode(QAbstractItemView.SelectionMode.MultiSelection)
        
        # 设置列宽
        header = self.users_table.horizontalHeader()
        header.setSectionResizeMode(0, QHeaderView.ResizeMode.Fixed)      # 选择
        header.setSectionResizeMode(1, QHeaderView.ResizeMode.Fixed)      # 用户ID
        header.setSectionResizeMode(2, QHeaderView.ResizeMode.ResizeToContents)  # 用户名
        header.setSectionResizeMode(3, QHeaderView.ResizeMode.ResizeToContents)  # 角色
        header.setSectionResizeMode(4, QHeaderView.ResizeMode.ResizeToContents)  # 创建时间
        header.setSectionResizeMode(5, QHeaderView.ResizeMode.ResizeToContents)  # 最后登录
        
        # 设置最小列宽
        self.users_table.setColumnWidth(0, 40)   # 选择
        self.users_table.setColumnWidth(1, 60)   # 用户ID
        
        # 连接选择变化信号
        self.users_table.itemSelectionChanged.connect(self.on_user_selection_changed)
        
        layout.addWidget(self.users_table)
        
        self.tab_widget.addTab(user_widget, "用户管理")
        
        # 加载用户数据
        self.load_users()
    
    def init_system_settings_tab(self):
        """初始化系统设置标签页"""
        settings_widget = QWidget()
        layout = QVBoxLayout(settings_widget)
        layout.setContentsMargins(5, 5, 5, 5)
        layout.setSpacing(5)
        
        # 创建滚动区域
        scroll_area_widget = QScrollArea()
        scroll_area_widget.setWidgetResizable(True)
        scroll_area_widget.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAsNeeded)
        scroll_area_widget.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAsNeeded)
        
        scroll_area = QWidget()
        scroll_layout = QVBoxLayout(scroll_area)
        scroll_layout.setContentsMargins(5, 5, 5, 5)
        scroll_layout.setSpacing(10)
        
        # 通信设置组
        comm_group = QGroupBox("仪器通信协议设置")
        comm_layout = QFormLayout(comm_group)
        
        # 协议类型选择 - 串口和TCP二选一
        self.comm_type_combo = QComboBox()
        self.comm_type_combo.addItems([CommunicationType.SERIAL.value, CommunicationType.TCP.value])
        self.comm_type_combo.setCurrentText(CommunicationType.TCP.value)  # 默认TCP
        self.comm_type_combo.currentTextChanged.connect(self.on_comm_type_changed)
        comm_layout.addRow("协议类型:", self.comm_type_combo)
        
        # TCP设置
        self.ip_edit = QLineEdit()
        self.ip_edit.setText("127.0.0.1")  # 默认IP
        self.ip_edit.setPlaceholderText("服务器IP地址")
        comm_layout.addRow("服务器IP:", self.ip_edit)
        
        self.port_spin = QSpinBox()
        self.port_spin.setRange(1, 65535)
        self.port_spin.setValue(5000)  # 默认端口
        comm_layout.addRow("端口:", self.port_spin)
        
        # 串口设置
        self.serial_port_combo = QComboBox()
        self.serial_port_combo.addItems(["COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9", "COM10"])
        self.serial_port_combo.setCurrentText("COM3")  # 默认COM3
        comm_layout.addRow("串口号:", self.serial_port_combo)
        
        self.baudrate_combo = QComboBox()
        self.baudrate_combo.addItems(["9600", "14400", "19200", "28800", "38400", "57600", "115200"])
        self.baudrate_combo.setCurrentText("115200")  # 默认115200
        comm_layout.addRow("波特率:", self.baudrate_combo)
        
        # 初始状态：隐藏串口设置，显示TCP设置
        self.serial_port_combo.setVisible(False)
        self.baudrate_combo.setVisible(False)
        
        self.comm_status_label = QLabel("未连接")
        self.comm_status_label.setStyleSheet("color: red; font-weight: bold;")
        comm_layout.addRow("连接状态:", self.comm_status_label)
        
        comm_button_layout = QHBoxLayout()
        self.connect_button = QPushButton("连接")
        self.connect_button.clicked.connect(self.toggle_connection)
        comm_button_layout.addWidget(self.connect_button)
        
        self.test_button = QPushButton("测试连接")
        self.test_button.clicked.connect(self.test_connection)
        comm_button_layout.addWidget(self.test_button)
        
        comm_layout.addRow("", comm_button_layout)
        
        scroll_layout.addWidget(comm_group)
        
        # TCP服务器设置组
        tcp_server_group = QGroupBox("TCP服务器设置")
        tcp_server_layout = QFormLayout(tcp_server_group)
        
        # TCP服务器地址
        self.tcp_server_host_edit = QLineEdit()
        self.tcp_server_host_edit.setText("0.0.0.0")  # 默认监听所有地址
        self.tcp_server_host_edit.setPlaceholderText("监听地址 (0.0.0.0 表示所有地址)")
        tcp_server_layout.addRow("监听地址:", self.tcp_server_host_edit)
        
        # TCP服务器端口
        self.tcp_server_port_spin = QSpinBox()
        self.tcp_server_port_spin.setRange(1, 65535)
        self.tcp_server_port_spin.setValue(5001)  # 默认端口5001，避免与客户端端口冲突
        tcp_server_layout.addRow("监听端口:", self.tcp_server_port_spin)
        
        # 最大客户端数
        self.tcp_server_max_clients_spin = QSpinBox()
        self.tcp_server_max_clients_spin.setRange(1, 100)
        self.tcp_server_max_clients_spin.setValue(10)  # 默认最大10个客户端
        tcp_server_layout.addRow("最大客户端数:", self.tcp_server_max_clients_spin)
        
        # TCP服务器状态
        self.tcp_server_status_label = QLabel("未连接")
        self.tcp_server_status_label.setStyleSheet("color: red; font-weight: bold;")
        tcp_server_layout.addRow("服务器状态:", self.tcp_server_status_label)
        
        # 连接客户端数
        self.tcp_server_client_count_label = QLabel("0")
        tcp_server_layout.addRow("连接客户端:", self.tcp_server_client_count_label)
        
        # TCP服务器控制按钮
        tcp_server_button_layout = QHBoxLayout()
        self.tcp_server_connect_button = QPushButton("连接")
        self.tcp_server_connect_button.clicked.connect(self.toggle_tcp_server)
        tcp_server_button_layout.addWidget(self.tcp_server_connect_button)
        
        self.tcp_server_test_button = QPushButton("测试连接")
        self.tcp_server_test_button.clicked.connect(self.test_tcp_server)
        tcp_server_button_layout.addWidget(self.tcp_server_test_button)
        
        tcp_server_layout.addRow("", tcp_server_button_layout)
        
        scroll_layout.addWidget(tcp_server_group)
        
        # 医院信息设置组
        hospital_group = QGroupBox("医院信息设置")
        hospital_layout = QFormLayout(hospital_group)
        
        self.hospital_name_edit = QLineEdit()
        hospital_layout.addRow("医院名称:", self.hospital_name_edit)
        
        self.hospital_address_edit = QLineEdit()
        hospital_layout.addRow("医院地址:", self.hospital_address_edit)
        
        self.hospital_contact_edit = QLineEdit()
        hospital_layout.addRow("联系方式:", self.hospital_contact_edit)
        
        self.logo_path_edit = QLineEdit()
        self.logo_path_edit.setPlaceholderText("选择医院LOGO文件")
        hospital_layout.addRow("医院LOGO:", self.logo_path_edit)
        
        logo_button_layout = QHBoxLayout()
        self.select_logo_button = QPushButton("选择文件")
        self.select_logo_button.clicked.connect(self.select_logo)
        logo_button_layout.addWidget(self.select_logo_button)
        
        hospital_layout.addRow("", logo_button_layout)
        
        scroll_layout.addWidget(hospital_group)
        
        # 警告区间设置组
        warning_group = QGroupBox("检测项目警告区间设置")
        warning_layout = QVBoxLayout(warning_group)
        
        # 警告区间表格
        self.warning_table = QTableWidget()
        self.warning_table.setColumnCount(5)
        self.warning_table.setHorizontalHeaderLabels(["检测项目", "最小值", "最大值", "单位", "警告颜色"])
        self.warning_table.setSelectionBehavior(QAbstractItemView.SelectionBehavior.SelectRows)
        self.warning_table.setAlternatingRowColors(True)
        
        # 设置列宽
        header = self.warning_table.horizontalHeader()
        header.setSectionResizeMode(0, QHeaderView.ResizeMode.Stretch)
        header.setSectionResizeMode(1, QHeaderView.ResizeMode.ResizeToContents)
        header.setSectionResizeMode(2, QHeaderView.ResizeMode.ResizeToContents)
        header.setSectionResizeMode(3, QHeaderView.ResizeMode.ResizeToContents)
        header.setSectionResizeMode(4, QHeaderView.ResizeMode.ResizeToContents)
        
        warning_layout.addWidget(self.warning_table)
        
        # 警告区间操作按钮
        warning_button_layout = QHBoxLayout()
        
        self.add_warning_button = QPushButton("添加项目")
        self.add_warning_button.clicked.connect(self.add_warning_range)
        warning_button_layout.addWidget(self.add_warning_button)
        
        self.edit_warning_button = QPushButton("编辑项目")
        self.edit_warning_button.clicked.connect(self.edit_warning_range)
        warning_button_layout.addWidget(self.edit_warning_button)
        
        self.delete_warning_button = QPushButton("删除项目")
        self.delete_warning_button.clicked.connect(self.delete_warning_range)
        warning_button_layout.addWidget(self.delete_warning_button)
        
        warning_layout.addLayout(warning_button_layout)
        
        scroll_layout.addWidget(warning_group)
        
        # 备份设置组
        backup_group = QGroupBox("备份设置")
        backup_layout = QVBoxLayout(backup_group)
        
        # 备份配置信息显示
        backup_info_layout = QFormLayout()
        
        self.backup_status_label = QLabel("未配置")
        self.backup_status_label.setStyleSheet("color: orange; font-weight: bold;")
        backup_info_layout.addRow("备份状态:", self.backup_status_label)
        
        self.backup_path_label = QLabel("未设置")
        backup_info_layout.addRow("备份路径:", self.backup_path_label)
        
        self.last_backup_label = QLabel("从未备份")
        backup_info_layout.addRow("最后备份:", self.last_backup_label)
        
        backup_layout.addLayout(backup_info_layout)
        
        # 备份操作按钮
        backup_button_layout = QHBoxLayout()
        
        self.backup_settings_button = QPushButton("备份设置")
        self.backup_settings_button.clicked.connect(self.open_backup_settings)
        backup_button_layout.addWidget(self.backup_settings_button)
        
        self.create_backup_button = QPushButton("立即备份")
        self.create_backup_button.clicked.connect(self.create_backup_now)
        backup_button_layout.addWidget(self.create_backup_button)
        
        # self.restore_backup_button = QPushButton("恢复备份")
        # self.restore_backup_button.clicked.connect(self.restore_backup)
        # backup_button_layout.addWidget(self.restore_backup_button)
        
        backup_layout.addLayout(backup_button_layout)
        
        scroll_layout.addWidget(backup_group)
        
        # 保存按钮
        save_button = QPushButton("保存设置")
        save_button.clicked.connect(self.save_settings)
        scroll_layout.addWidget(save_button)
        
        scroll_layout.addStretch()
        
        # 将滚动区域添加到布局
        scroll_area_widget.setWidget(scroll_area)
        layout.addWidget(scroll_area_widget)
        
        self.tab_widget.addTab(settings_widget, "系统设置")
        
        # 加载设置
        self.load_settings()
        self.load_warning_ranges()
        
        # 更新备份状态
        self.update_backup_status()
        
        # 初始化通信控件可见性
        self.init_comm_controls_visibility()
    
    def init_comm_controls_visibility(self):
        """初始化通信控件可见性 - 根据当前通信类型设置控件显示状态"""
        try:
            # 获取当前通信类型
            current_type = self.comm_type_combo.currentText()
            
            # 根据通信类型设置控件可见性
            if current_type == CommunicationType.SERIAL.value:
                # 串口模式：隐藏TCP设置，显示串口设置
                if hasattr(self, 'ip_edit'):
                    self.ip_edit.setVisible(False)
                if hasattr(self, 'port_spin'):
                    self.port_spin.setVisible(False)
                if hasattr(self, 'serial_port_combo'):
                    self.serial_port_combo.setVisible(True)
                if hasattr(self, 'baudrate_combo'):
                    self.baudrate_combo.setVisible(True)
            else:
                # TCP模式：显示TCP设置，隐藏串口设置
                if hasattr(self, 'ip_edit'):
                    self.ip_edit.setVisible(True)
                if hasattr(self, 'port_spin'):
                    self.port_spin.setVisible(True)
                if hasattr(self, 'serial_port_combo'):
                    self.serial_port_combo.setVisible(False)
                if hasattr(self, 'baudrate_combo'):
                    self.baudrate_combo.setVisible(False)
                    
            logger.info(f"通信控件可见性初始化完成，当前类型: {current_type}")
            
        except Exception as e:
            logger.error(f"初始化通信控件可见性失败: {e}")
        try:
            if hasattr(self, 'comm_type_combo'):
                # 根据当前选择的通信类型设置控件可见性
                current_comm_type = self.comm_type_combo.currentText()
                self.on_comm_type_changed(current_comm_type)
        except Exception as e:
            logger.error(f"初始化通信控件可见性失败: {e}")
    
    def load_samples(self):
        """加载样本数据 - 使用优化的分页加载"""
        try:
            if not hasattr(self, 'sample_table'):
                logger.warning("样本表格控件未初始化")
                return
            
            # 显示加载状态
            if hasattr(self, 'status_bar'):
                self.status_bar.showMessage("正在加载样本数据...")
            
            # 使用优化的分页管理器加载第一页数据
            self.sample_pagination_manager.go_to_page(1)
            
            # 获取当前页数据并填充表格
            current_samples = self.sample_pagination_manager.get_current_data()
            self.fill_sample_table(current_samples)
            
            # 更新分页控件
            if hasattr(self, 'sample_pagination_widget'):
                self.sample_pagination_widget.update_ui()
            
            # 自动选择第一行并显示预览
            if len(current_samples) > 0:
                self.sample_table.selectRow(0)
                self.on_sample_selection_changed()
            
            # 获取分页信息
            pagination_info = self.sample_pagination_manager.get_pagination_info()
            
            # 更新状态栏
            if hasattr(self, 'status_bar'):
                self.status_bar.showMessage(f"已加载 {pagination_info.total_items} 个样本，当前显示第 {pagination_info.current_page} 页")
            
            logger.info(f"样本数据加载完成，共 {pagination_info.total_items} 个样本")
            
        except Exception as e:
            logger.error(f"加载样本失败: {e}")
            QMessageBox.critical(self, "错误", f"加载样本失败: {e}")
            if hasattr(self, 'status_bar'):
                self.status_bar.showMessage("样本数据加载失败")
    
    def format_percentage_results(self, results) -> str:
        """格式化百分比结果"""
        try:
            formatted_results = []
            
            # 基础检测项目 - 显示所有项目，包括0.00值
            basic_items = [
                ('HbA1a', results.HbA1a),
                ('HbA1b', results.HbA1b),
                ('HbF', results.HbF),
                ('LA1c', results.LA1c),  # 修正字段名
                ('HbA1c', results.HbA1c),
                ('HbA0', results.HbA0),
                ('eAG', results.eAG)
            ]
            
            for name, value in basic_items:
                # 显示所有值，包括0.00和空值
                if value is not None and value != "":
                    formatted_results.append(f"{name}: {value}")
                else:
                    formatted_results.append(f"{name}: 0.00")
            
            # 变异筛查项目 - 显示所有项目，包括0.00值
            variant_items = [
                ('A2', results.A2),
                ('HbE', results.HbE),
                ('HbD', results.HbD),
                ('HbS', results.HbS),
                ('HbC', results.HbC),
                ('p3', results.p3),
                ('v-win', results.v_win)
            ]
            
            for name, value in variant_items:
                # 显示所有值，包括0.00和空值
                if value is not None and value != "":
                    formatted_results.append(f"{name}: {value}")
                else:
                    formatted_results.append(f"{name}: 0.00")
            
            return " | ".join(formatted_results)
            
        except Exception as e:
            logger.error(f"格式化百分比结果失败: {e}")
            return "结果格式化失败"
    
    def format_time_data(self, results) -> str:
        """格式化TIME数据"""
        try:
            formatted_results = []
            
            # TIME数据
            if results.TIME and len(results.TIME) > 0:
                time_values = [str(t) for t in results.TIME if t]
                if time_values:
                    formatted_results.append(f"TIME: {', '.join(time_values)}")
            
            return " | ".join(formatted_results)
            
        except Exception as e:
            logger.error(f"格式化TIME数据失败: {e}")
            return "结果格式化失败"
    
    def format_area_data(self, results) -> str:
        """格式化AREA数据"""
        try:
            formatted_results = []
            
            # AREA数据
            if results.AREA and len(results.AREA) > 0:
                area_values = [str(a) for a in results.AREA if a]
                if area_values:
                    formatted_results.append(f"AREA: {', '.join(area_values)}")
            
            return " | ".join(formatted_results)
            
        except Exception as e:
            logger.error(f"格式化AREA数据失败: {e}")
            return "结果格式化失败"
    

    
    def open_advanced_search(self):
        """打开高级搜索对话框"""
        try:
            # 导入高级搜索对话框
            try:
                from src.ui.advanced_search_dialog import AdvancedSearchDialog
            except ImportError:
                try:
                    from .advanced_search_dialog import AdvancedSearchDialog
                except ImportError:
                    QMessageBox.critical(self, "错误", "无法导入高级搜索对话框模块")
                    return
            
            # 创建高级搜索对话框
            dialog = AdvancedSearchDialog(self)
            dialog.search_requested.connect(self.perform_advanced_search)
            dialog.exec()
            
        except Exception as e:
            logger.error(f"打开高级搜索对话框失败: {e}")
            QMessageBox.critical(self, "错误", f"打开高级搜索对话框失败: {e}")
    
    def perform_advanced_search(self, conditions: dict):
        """执行高级搜索"""
        try:
            if not conditions:
                QMessageBox.warning(self, "警告", "请至少输入一个搜索条件")
                return
            
            # 使用分页管理器的高级搜索功能
            if self.sample_pagination_manager.advanced_search(conditions):
                # 获取当前页数据并填充表格
                current_samples = self.sample_pagination_manager.get_current_data()
                self.fill_sample_table(current_samples)
                
                # 更新分页控件
                if hasattr(self, 'sample_pagination_widget'):
                    self.sample_pagination_widget.update_ui()
                
                # 自动选择第一行并显示预览
                if len(current_samples) > 0:
                    self.sample_table.selectRow(0)
                    self.on_sample_selection_changed()
                
                # 显示搜索条件摘要
                condition_summary = self.format_search_conditions(conditions)
                if hasattr(self, 'status_bar'):
                    pagination_info = self.sample_pagination_manager.get_pagination_info()
                    self.status_bar.showMessage(f"高级搜索完成: 找到 {pagination_info.total_items} 个样本，当前显示第 {pagination_info.current_page} 页 | 条件: {condition_summary}")
                
                # 记录搜索操作日志
                if self.current_user:
                    log = OperationLog(
                        id=0,
                        operation_time=datetime.now(),
                        operator=self.current_user.username,
                        operation_type="高级搜索",
                        operation_content=f"高级搜索: {condition_summary}, 找到 {pagination_info.total_items} 个样本",
                        ip_address="127.0.0.1"
                    )
                    self.db_manager.add_operation_log(log)
                
                logger.info(f"高级搜索成功: 找到 {pagination_info.total_items} 个样本")
            else:
                logger.warning("高级搜索失败")
                if hasattr(self, 'status_bar'):
                    self.status_bar.showMessage("高级搜索失败，请重试")
            
        except Exception as e:
            logger.error(f"执行高级搜索失败: {e}")
            QMessageBox.critical(self, "错误", f"高级搜索失败: {e}")
    
    def format_search_conditions(self, conditions: dict) -> str:
        """格式化搜索条件为可读字符串"""
        try:
            condition_parts = []
            
            if 'sample_number' in conditions:
                condition_parts.append(f"样本编号: {conditions['sample_number']}")
            
            if 'patient_name' in conditions:
                condition_parts.append(f"患者姓名: {conditions['patient_name']}")
            
            if 'patient_gender' in conditions:
                condition_parts.append(f"性别: {conditions['patient_gender']}")
            
            if 'age_range' in conditions:
                age_min, age_max = conditions['age_range']
                condition_parts.append(f"年龄: {age_min}-{age_max}岁")
            
            if 'time_range' in conditions:
                start_time, end_time = conditions['time_range']
                condition_parts.append(f"时间: {start_time.strftime('%Y-%m-%d %H:%M')} 至 {end_time.strftime('%Y-%m-%d %H:%M')}")
            
            if 'machine_model' in conditions:
                condition_parts.append(f"仪器型号: {conditions['machine_model']}")
            
            if 'machine_id' in conditions:
                condition_parts.append(f"仪器编号: {conditions['machine_id']}")
            
            if 'analysis_mode' in conditions:
                condition_parts.append(f"分析模式: {conditions['analysis_mode']}")
            
            if 'protocol' in conditions:
                condition_parts.append(f"协议: {conditions['protocol']}")
            
            if 'status' in conditions:
                condition_parts.append(f"状态: {conditions['status']}")
            
            return "; ".join(condition_parts) if condition_parts else "无"
            
        except Exception as e:
            logger.error(f"格式化搜索条件失败: {e}")
            return "条件格式化失败"
    
    def add_sample(self):
        """新增样本 - 需要管理员权限"""
        try:
            # 检查权限
            if not self._check_permission(UserRole.ADMIN):
                return
            dialog = SampleEditDialog(self)
            if dialog.exec() == QDialog.DialogCode.Accepted:
                sample = dialog.get_sample()
                
                # 验证样本数据
                sample_number = getattr(sample.sample_info, 'sample_number', '').strip()
                patient_name = getattr(sample.sample_info, 'patient_name', '').strip()
                
                if not sample_number:
                    QMessageBox.warning(self, "警告", "请输入样本编号")
                    return
                
                if not patient_name:
                    QMessageBox.warning(self, "警告", "请输入患者姓名")
                    return
                
                # 添加样本到数据库
                if self.db_manager.add_sample(sample):
                    # 记录操作日志
                    log = OperationLog(
                        id=0,
                        operation_time=datetime.now(),
                        operator=self.current_user.username if self.current_user else "未知用户",
                        operation_type="新增样本",
                        operation_content=f"新增样本: {sample_number}, 患者: {patient_name}",
                        ip_address="127.0.0.1"  # 实际应用中应获取真实IP
                    )
                    self.db_manager.add_operation_log(log)
                    
                    # 更新备份检查点
                    self.update_backup_checkpoint()
                    
                    # 刷新样本列表
                    self.load_samples()
                    QMessageBox.information(self, "成功", f"样本 {sample_number} 添加成功")
                else:
                    QMessageBox.critical(self, "错误", "样本添加失败，请检查数据格式")
        except Exception as e:
            logger.error(f"新增样本失败: {e}")
            QMessageBox.critical(self, "错误", f"新增样本失败: {e}")
    
    def edit_sample(self):
        """编辑样本 - 需要管理员权限"""
        try:
            # 检查权限
            if not self._check_permission(UserRole.ADMIN):
                return
            current_row = self.sample_table.currentRow()
            if current_row < 0:
                QMessageBox.warning(self, "警告", "请选择要编辑的样本")
                return
            
            # 使用新的方法获取样本数据 - 通过隐藏的ID列
            sample = self.get_sample_by_row(current_row)
            if not sample:
                QMessageBox.warning(self, "警告", "无法获取选中的样本数据")
                return
            
            logger.info(f"开始编辑样本: ID={sample.id}, 样本编号={sample.sample_info.sample_number}, 患者姓名={sample.sample_info.patient_name}")
            
            from ui.dialogs import SampleEditDialog
            dialog = SampleEditDialog(self, sample)
            if dialog.exec() == QDialog.DialogCode.Accepted:
                updated_sample = dialog.get_sample()
                if self.db_manager.update_sample(updated_sample):
                    self.load_samples()
                    QMessageBox.information(self, "成功", f"样本 {sample.sample_info.sample_number} (ID: {sample.id}) 更新成功")
                else:
                    QMessageBox.critical(self, "错误", f"样本 {sample.sample_info.sample_number} (ID: {sample.id}) 更新失败")
        except Exception as e:
            logger.error(f"编辑样本失败: {e}")
            QMessageBox.critical(self, "错误", f"编辑样本失败: {e}")
    
    def edit_patient_info(self):
        """编辑患者信息 - 需要管理员权限"""
        try:
            # 检查权限
            if not self._check_permission(UserRole.ADMIN):
                return
            current_row = self.sample_table.currentRow()
            if current_row < 0:
                QMessageBox.warning(self, "警告", "请选择要编辑患者信息的样本")
                return
            
            # 使用新的方法获取样本数据 - 通过隐藏的ID列
            sample = self.get_sample_by_row(current_row)
            if not sample:
                QMessageBox.warning(self, "警告", "无法获取选中的样本数据")
                return
            
            logger.info(f"开始编辑患者信息: ID={sample.id}, 样本编号={sample.sample_info.sample_number}, 患者姓名={sample.sample_info.patient_name}")
            
            from ui.dialogs import PatientInfoDialog
            dialog = PatientInfoDialog(
                self, 
                sample.sample_info.patient_name,
                sample.sample_info.patient_age,
                sample.sample_info.patient_gender
            )
            
            if dialog.exec() == QDialog.DialogCode.Accepted:
                patient_info = dialog.get_patient_info()
                
                # 更新样本的患者信息
                sample.sample_info.patient_name = patient_info["patient_name"]
                sample.sample_info.patient_age = patient_info["patient_age"]
                sample.sample_info.patient_gender = patient_info["patient_gender"]
                
                if self.db_manager.update_sample(sample):
                    self.load_samples()
                    QMessageBox.information(self, "成功", f"患者信息更新成功 (样本: {sample.sample_info.sample_number}, ID: {sample.id})")
                else:
                    QMessageBox.critical(self, "错误", f"患者信息更新失败 (样本: {sample.sample_info.sample_number}, ID: {sample.id})")
        except Exception as e:
            logger.error(f"编辑患者信息失败: {e}")
            QMessageBox.critical(self, "错误", f"编辑患者信息失败: {e}")
    
    def get_sample_by_row(self, row: int):
        """根据行号获取样本数据 - 使用隐藏的ID列作为数据库索引"""
        try:
            # 从隐藏的ID列（第0列）获取样本ID
            sample_id_item = self.sample_table.item(row, 0)  # ID列在第0列，完全隐藏
            if not sample_id_item:
                logger.error(f"无法获取第{row}行的样本ID")
                return None
            
            # 获取样本ID（存储在UserRole中）
            sample_id = sample_id_item.data(Qt.ItemDataRole.UserRole)
            if not sample_id:
                logger.error(f"第{row}行的样本ID为空")
                return None
            
            # 使用ID作为索引从数据库重新获取样本数据，确保数据是最新的
            sample = self.db_manager.get_sample_by_id(sample_id)
            if not sample:
                logger.error(f"无法从数据库获取样本ID: {sample_id}")
                return None
            
            logger.info(f"成功通过ID获取样本: ID={sample_id}, 样本编号={sample.sample_info.sample_number}, 患者姓名={sample.sample_info.patient_name}")
            return sample
            
        except Exception as e:
            logger.error(f"根据行号获取样本失败: {e}")
            return None
    

    
    def edit_sample_dialog(self, sample: Sample):
        """编辑样本对话框 - 使用ID作为数据库索引 - 需要管理员权限"""
        try:
            # 检查权限
            if not self._check_permission(UserRole.ADMIN):
                return
                
            # 保存原始样本数据用于对比 - 使用新的数据结构
            from src.models.models import SampleInfo, Results, ImageInfo, ModeInfo
            
            # 确保使用正确的ID
            sample_id = sample.id
            logger.info(f"编辑样本对话框: 使用ID={sample_id}作为数据库索引")
            
            # 创建原始样本的副本用于对比
            original_sample_info = SampleInfo(
                sample_id=sample.sample_info.sample_id,
                sample_number=sample.sample_info.sample_number,
                patient_name=sample.sample_info.patient_name,
                patient_age=sample.sample_info.patient_age,
                patient_gender=sample.sample_info.patient_gender,
                datetime=sample.sample_info.datetime,
                sample_type=sample.sample_info.sample_type,
                sample_type_name=sample.sample_info.sample_type_name,
                sample_category=sample.sample_info.sample_category,
                report_id=sample.sample_info.report_id,
                position=sample.sample_info.position
            )
            
            original_sample = Sample(
                id=sample_id,  # 确保使用正确的ID
                protocol=sample.protocol,
                machine_model=sample.machine_model,
                machine_id=sample.machine_id,
                sample_info=original_sample_info,
                results=sample.results,
                image=sample.image,
                analysis_mode=sample.analysis_mode,
                mode_info=sample.mode_info,
                raw=sample.raw,
                receive_time=sample.receive_time,
                status=sample.status,
                created_time=sample.created_time,
                updated_time=sample.updated_time
            )
            
            dialog = SampleEditDialog(self, sample)
            if dialog.exec() == QDialog.DialogCode.Accepted:
                updated_sample = dialog.get_sample()
                
                # 验证样本数据
                sample_number = getattr(updated_sample.sample_info, 'sample_number', '').strip()
                patient_name = getattr(updated_sample.sample_info, 'patient_name', '').strip()
                
                if not sample_number:
                    QMessageBox.warning(self, "警告", "请输入样本编号")
                    return
                
                if not patient_name:
                    QMessageBox.warning(self, "警告", "请输入患者姓名")
                    return
                
                # 生成详细的修改内容
                changes = self.generate_sample_changes(original_sample, updated_sample)
                
                # 确保更新样本时使用正确的ID
                updated_sample.id = sample_id  # 确保ID正确
                logger.info(f"准备更新样本: ID={sample_id}, 样本编号={sample_number}, 患者姓名={patient_name}")
                
                # 更新样本
                if self.db_manager.update_sample(updated_sample):
                    # 记录操作日志
                    log = OperationLog(
                        id=0,
                        operation_time=datetime.now(),
                        operator=self.current_user.username if self.current_user else "未知用户",
                        operation_type="编辑样本",
                        operation_content=f"编辑样本: ID={sample_id}, 样本编号={sample_number}, 患者: {patient_name}\n修改内容: {changes}",
                        ip_address="127.0.0.1"  # 实际应用中应获取真实IP
                    )
                    self.db_manager.add_operation_log(log)
                    
                    # 更新备份检查点
                    self.update_backup_checkpoint()
                    
                    # 刷新样本列表
                    self.load_samples()
                    QMessageBox.information(self, "成功", f"样本 {sample_number} (ID: {sample_id}) 更新成功")
                else:
                    QMessageBox.critical(self, "错误", f"样本更新失败，请检查数据格式 (ID: {sample_id})")
        except Exception as e:
            logger.error(f"编辑样本对话框失败: {e}")
            QMessageBox.critical(self, "错误", f"编辑样本对话框失败: {e}")
    
    def generate_sample_changes(self, original: Sample, updated: Sample) -> str:
        """生成样本修改的详细内容"""
        changes = []
        
        # 基本信息对比 - 使用新的数据结构
        if original.sample_info.sample_number != updated.sample_info.sample_number:
            changes.append(f"样本编号: {original.sample_info.sample_number} → {updated.sample_info.sample_number}")
        
        if original.machine_id != updated.machine_id:
            changes.append(f"仪器ID: {original.machine_id} → {updated.machine_id}")
        
        if original.machine_model != updated.machine_model:
            changes.append(f"仪器型号: {original.machine_model} → {updated.machine_model}")
        
        if original.sample_info.position != updated.sample_info.position:
            changes.append(f"样本位置: {original.sample_info.position} → {updated.sample_info.position}")
        
        # 患者信息对比
        if original.sample_info.patient_name != updated.sample_info.patient_name:
            changes.append(f"患者姓名: {original.sample_info.patient_name} → {updated.sample_info.patient_name}")
        
        if original.sample_info.patient_age != updated.sample_info.patient_age:
            changes.append(f"患者年龄: {original.sample_info.patient_age} → {updated.sample_info.patient_age}")
        
        if original.sample_info.patient_gender != updated.sample_info.patient_gender:
            changes.append(f"患者性别: {original.sample_info.patient_gender} → {updated.sample_info.patient_gender}")
        
        # 检测信息对比
        if original.protocol != updated.protocol:
            changes.append(f"协议类型: {original.protocol} → {updated.protocol}")
        
        if original.analysis_mode != updated.analysis_mode:
            changes.append(f"分析模式: {original.analysis_mode} → {updated.analysis_mode}")
        
        # 检测结果对比
        if original.results.HbA1c != updated.results.HbA1c:
            changes.append(f"HbA1c: {original.results.HbA1c} → {updated.results.HbA1c}")
        
        if original.results.LA1c != updated.results.LA1c:
            changes.append(f"LA1c: {original.results.LA1c} → {updated.results.LA1c}")
        
        if original.results.HbA0 != updated.results.HbA0:
            changes.append(f"HbA0: {original.results.HbA0} → {updated.results.HbA0}")
        
        if original.sample_info.report_id != updated.sample_info.report_id:
            changes.append(f"报告ID: {original.sample_info.report_id} → {updated.sample_info.report_id}")
        
        if original.status != updated.status:
            changes.append(f"状态: {original.status.value} → {updated.status.value}")
        
        if not changes:
            return "无修改"
        
        return "; ".join(changes)
    
    def export_samples(self):
        """导出样本（快速导出选中样本）- 需要管理员权限"""
        try:
            # 检查权限
            if not self._check_permission(UserRole.ADMIN):
                return
                
            if not hasattr(self, 'sample_table'):
                QMessageBox.warning(self, "警告", "样本表格未初始化")
                return
                
            selected_samples = self.get_selected_samples()
            if not selected_samples:
                QMessageBox.warning(self, "警告", "请选择要导出的样本")
                return
            
            # 选择保存路径
            timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
            default_filename = f"样本数据_{timestamp}.xlsx"
            
            file_path, _ = QFileDialog.getSaveFileName(
                self, "导出样本", default_filename, "Excel文件 (*.xlsx)"
            )
            
            if file_path:
                # 确保文件扩展名正确
                if not file_path.endswith('.xlsx'):
                    file_path += '.xlsx'
                
                # 执行导出
                success = self.export_manager.export_samples_to_excel(selected_samples, file_path, include_all_data=True)
                
                if success:
                    QMessageBox.information(self, "导出成功", f"成功导出 {len(selected_samples)} 个样本到: {file_path}")
                    
                    # 记录操作日志
                    if self.current_user:
                        log = OperationLog(
                            id=0,
                            operation_time=datetime.now(),
                            operator=self.current_user.username,
                            operation_type="快速导出样本",
                            operation_content=f"快速导出样本: {len(selected_samples)} 个样本到 {file_path}",
                            ip_address="127.0.0.1"
                        )
                        self.db_manager.add_operation_log(log)
                else:
                    QMessageBox.critical(self, "导出失败", "样本导出失败，请检查文件路径和权限")
        except Exception as e:
            logger.error(f"导出样本失败: {e}")
            QMessageBox.critical(self, "错误", f"导出样本失败: {e}")
    
    def batch_export_samples(self):
        """批量导出样本 - 需要管理员权限"""
        try:
            # 检查权限
            if not self._check_permission(UserRole.ADMIN):
                return
            if not hasattr(self, 'sample_table'):
                QMessageBox.warning(self, "警告", "样本表格未初始化")
                return
            
            # 获取所有样本数据
            all_samples = self.db_manager.get_samples()
            
            if not all_samples:
                QMessageBox.warning(self, "警告", "没有样本数据可导出")
                return
            
            # 获取当前页样本数据
            current_page_samples = self.sample_pagination_manager.get_current_data()
            
            # 获取选中的样本数据
            selected_samples = self.get_selected_samples()
            
            # 打开批量导出对话框
            dialog = BatchExportDialog(
                all_samples, 
                self, 
                current_page_samples=current_page_samples,
                selected_samples=selected_samples
            )
            if dialog.exec() == QDialog.DialogCode.Accepted:
                # 导出成功，记录操作日志
                if self.current_user:
                    log = OperationLog(
                        id=0,
                        operation_time=datetime.now(),
                        operator=self.current_user.username,
                        operation_type="批量导出样本",
                        operation_content=f"批量导出样本: 通过批量导出对话框导出样本数据",
                        ip_address="127.0.0.1"
                    )
                    self.db_manager.add_operation_log(log)
                    
        except Exception as e:
            logger.error(f"批量导出样本失败: {e}")
            QMessageBox.critical(self, "错误", f"批量导出样本失败: {e}")
    
    def send_samples(self):
        """批量发送样本 - 需要管理员权限"""
        try:
            # 检查权限
            if not self._check_permission(UserRole.ADMIN):
                return
            # 检查TCP服务器是否运行
            if not self.comm_manager.is_tcp_server_running():
                QMessageBox.warning(self, "警告", "TCP服务器未运行，请先在系统设置中启动TCP服务器")
                return

            # 检查是否有客户端连接
            if self.comm_manager.get_tcp_server_client_count() == 0:
                QMessageBox.warning(self, "警告", "没有客户端连接，无法发送数据")
                return
            
            # 打开批量发送对话框
            dialog = BatchSendDialog(
                push_service=self.sample_push_service, 
                parent=self,
                samples=self.db_manager.get_samples(),  # 获取全部样本数据
                current_page_samples=self.sample_pagination_manager.get_current_data(),  # 传递当前页样本
                selected_samples=self.get_selected_samples()  # 传递选中的样本
            )
            dialog.push_completed.connect(self.on_batch_send_completed)
            dialog.exec()
            
        except Exception as e:
            logger.error(f"批量发送样本失败: {e}")
            QMessageBox.critical(self, "错误", f"批量发送样本失败: {e}")
    
    def on_batch_send_completed(self, result: dict):
        """批量发送完成回调"""
        try:
            success_count = result.get("success", 0)
            total_count = result.get("total", 0)
            
            # 记录操作日志
            if self.current_user:
                log = OperationLog(
                    id=0,
                    operation_time=datetime.now(),
                    operator=self.current_user.username,
                    operation_type="批量发送",
                    operation_content=f"批量发送样本: 成功 {success_count}/{total_count}",
                    ip_address="127.0.0.1"
                )
                self.db_manager.add_operation_log(log)
            
            logger.info(f"批量发送完成: 成功 {success_count}/{total_count}")
            
        except Exception as e:
            logger.error(f"处理批量发送完成回调失败: {e}")
    

    
    def get_selected_samples(self):
        """获取选中的样本列表（支持多选）"""
        try:
            selected_samples = []
            selected_rows = self.sample_table.selectedItems()
            
            if not selected_rows:
                return []
            
            # 获取选中的行号（去重）
            selected_row_numbers = set()
            for item in selected_rows:
                selected_row_numbers.add(item.row())
            
            # 获取当前页的样本数据
            current_samples = self.sample_pagination_manager.get_current_data()
            
            # 根据行号获取对应的样本
            for row_num in selected_row_numbers:
                if 0 <= row_num < len(current_samples):
                    selected_samples.append(current_samples[row_num])
            
            return selected_samples
        except Exception as e:
            logger.error(f"获取选中样本失败: {e}")
            return []
    
    def select_all_samples(self):
        """全选样本"""
        try:
            self.sample_table.selectAll()
            self.update_selected_count()
        except Exception as e:
            logger.error(f"全选样本失败: {e}")
    
    def clear_sample_selection(self):
        """清除样本选择"""
        try:
            self.sample_table.clearSelection()
            self.update_selected_count()
        except Exception as e:
            logger.error(f"清除样本选择失败: {e}")
    
    def invert_sample_selection(self):
        """反选样本"""
        try:
            # 获取所有行
            total_rows = self.sample_table.rowCount()
            if total_rows == 0:
                return
            
            # 获取当前选中的行
            selected_rows = set()
            for item in self.sample_table.selectedItems():
                selected_rows.add(item.row())
            
            # 清除当前选择
            self.sample_table.clearSelection()
            
            # 选择未选中的行
            for row in range(total_rows):
                if row not in selected_rows:
                    self.sample_table.selectRow(row)
            
            self.update_selected_count()
        except Exception as e:
            logger.error(f"反选样本失败: {e}")
    
    def update_selected_row_style(self):
        """更新选中行的字体颜色为白色"""
        try:
            # 获取所有选中的行
            selected_rows = self.sample_table.selectionModel().selectedRows()
            selected_row_indices = [index.row() for index in selected_rows]
            
            # 遍历所有行，更新字体颜色
            for row in range(self.sample_table.rowCount()):
                for col in range(self.sample_table.columnCount()):
                    item = self.sample_table.item(row, col)
                    if item:
                        if row in selected_row_indices:
                            # 选中的行：设置字体颜色为白色
                            item.setForeground(Qt.GlobalColor.white)
                        else:
                            # 未选中的行：恢复默认字体颜色
                            item.setForeground(Qt.GlobalColor.black)
            
            # 更新选中数量显示
            self.update_selected_count()
            
        except Exception as e:
            logger.error(f"更新选中行样式失败: {e}")
    
    def update_selected_count(self):
        """更新选中样本数量显示"""
        try:
            selected_count = len(self.get_selected_samples())
            self.selected_count_label.setText(str(selected_count))
            
            # 根据选中数量调整按钮状态
            has_selection = selected_count > 0
            self.export_button.setEnabled(has_selection)
            
        except Exception as e:
            logger.error(f"更新选中数量失败: {e}")
    
    def on_sample_selection_changed(self):
        """样本选择变化时的回调"""
        try:
            self.update_selected_count()
        except Exception as e:
            logger.error(f"处理样本选择变化失败: {e}")
    
    def view_image(self, image_path: str):
        """查看图片"""
        try:
            if not os.path.exists(image_path):
                QMessageBox.warning(self, "警告", "图片文件不存在")
                return
            
            dialog = ImageViewerDialog(image_path, self)
            dialog.exec()
        except Exception as e:
            logger.error(f"查看图片失败: {e}")
            QMessageBox.critical(self, "错误", f"查看图片失败: {e}")
    

    
    def load_logs(self):
        """加载系统日志"""
        try:
            if not hasattr(self, 'logs_table'):
                logger.warning("日志表格控件未初始化")
                return
                
            # 获取所有日志数据
            all_logs = self.db_manager.get_operation_logs()
            logger.info(f"从数据库获取到 {len(all_logs)} 条操作日志")
            
            # 设置分页管理器数据
            self.logs_pagination_manager.set_data(all_logs)
            
            # 获取当前页数据并填充表格
            current_logs = self.logs_pagination_manager.get_current_data()
            logger.info(f"当前页显示 {len(current_logs)} 条日志")
            self.fill_logs_table(current_logs)
            
            # 更新分页控件
            if hasattr(self, 'logs_pagination_widget'):
                self.logs_pagination_widget.update_ui()
            
            if hasattr(self, 'status_bar'):
                pagination_info = self.logs_pagination_manager.get_pagination_info()
                self.status_bar.showMessage(f"已加载 {pagination_info.total_items} 条日志，当前显示第 {pagination_info.current_page} 页")
            
        except Exception as e:
            logger.error(f"加载日志失败: {e}")
            QMessageBox.critical(self, "错误", f"加载日志失败: {e}")
    
    def export_logs(self):
        """导出日志"""
        try:
            file_path, _ = QFileDialog.getSaveFileName(
                self, "导出日志", "", "Excel文件 (*.xlsx)"
            )
            
            if file_path:
                # 这里实现导出日志功能
                QMessageBox.information(self, "提示", "导出日志功能待实现")
        except Exception as e:
            logger.error(f"导出日志失败: {e}")
            QMessageBox.critical(self, "错误", f"导出日志失败: {e}")
    
    def on_comm_type_changed(self, text: str):
        """通信类型改变 - 串口和TCP二选一互斥"""
        try:
            if hasattr(self, 'ip_edit') and hasattr(self, 'port_spin') and hasattr(self, 'serial_port_combo') and hasattr(self, 'baudrate_combo'):
                if text == CommunicationType.SERIAL.value:
                    # 串口模式，隐藏IP和端口设置，显示串口设置
                    self.ip_edit.setVisible(False)
                    self.port_spin.setVisible(False)
                    self.serial_port_combo.setVisible(True)
                    self.baudrate_combo.setVisible(True)
                    
                    # 设置通信管理器为串口模式
                    if hasattr(self, 'comm_manager'):
                        self.comm_manager.set_communication_type(CommunicationType.SERIAL)
                        logger.info("通信类型已切换为串口模式")
                else:
                    # TCP模式，显示IP和端口设置，隐藏串口设置
                    self.ip_edit.setVisible(True)
                    self.port_spin.setVisible(True)
                    self.serial_port_combo.setVisible(False)
                    self.baudrate_combo.setVisible(False)
                    
                    # 设置通信管理器为TCP模式
                    if hasattr(self, 'comm_manager'):
                        self.comm_manager.set_communication_type(CommunicationType.TCP)
                        logger.info("通信类型已切换为TCP模式")
        except Exception as e:
            logger.error(f"通信类型切换失败: {e}")
    
    def toggle_connection(self):
        """切换连接状态 - 支持串口和TCP二选一互斥模式"""
        try:
            # 检查权限
            if not self._check_permission(UserRole.ADMIN):
                return
                
            if not self.comm_manager.is_connected:
                # 连接
                comm_type = self.comm_type_combo.currentText()
                
                if comm_type == CommunicationType.SERIAL.value:
                    # 串口模式
                    if hasattr(self, 'serial_port_combo') and hasattr(self, 'baudrate_combo'):
                        serial_port = self.serial_port_combo.currentText()
                        baudrate = int(self.baudrate_combo.currentText())
                        
                        # 设置通信类型为串口
                        self.comm_manager.set_communication_type(CommunicationType.SERIAL)
                        success = self.comm_manager.setup_serial_port(serial_port, baudrate)
                        
                        if success:
                            logger.info(f"串口设置成功: {serial_port}@{baudrate}")
                        else:
                            QMessageBox.critical(self, "错误", "串口设置失败")
                            return
                    else:
                        QMessageBox.critical(self, "错误", "串口控件未找到")
                        return
                else:
                    # TCP模式
                    if hasattr(self, 'ip_edit') and hasattr(self, 'port_spin'):
                        host = self.ip_edit.text().strip()
                        port = self.port_spin.value()
                        
                        if not host:
                            QMessageBox.warning(self, "警告", "请输入服务器IP地址")
                            return
                        
                        # 设置通信类型为TCP
                        self.comm_manager.set_communication_type(CommunicationType.TCP)
                        success = self.comm_manager.setup_tcp_client(host, port)
                        
                        if success:
                            logger.info(f"TCP客户端设置成功: {host}:{port}")
                        else:
                            QMessageBox.critical(self, "错误", "TCP客户端设置失败")
                            return
                    else:
                        QMessageBox.critical(self, "错误", "TCP控件未找到")
                        return
                
                # 启动通信
                if self.comm_manager.start_communication():
                    # 连接信号到数据处理方法
                    if hasattr(self.comm_manager, 'comm_thread') and self.comm_manager.comm_thread:
                        # 检查线程是否有message_processed信号
                        if hasattr(self.comm_manager.comm_thread, 'message_processed'):
                            try:
                                self.comm_manager.comm_thread.message_processed.connect(self.on_data_received)
                                logger.info("成功连接message_processed信号")
                            except Exception as e:
                                logger.error(f"连接message_processed信号失败: {e}")
                        else:
                            logger.warning("通信线程没有message_processed信号")
                    
                    # 注册TCP重连回调函数（仅TCP模式）
                    if comm_type == CommunicationType.TCP.value and hasattr(self.comm_manager, 'tcp_client') and self.comm_manager.tcp_client:
                        try:
                            self.comm_manager.tcp_client.add_reconnect_callback(self.on_tcp_reconnect_success)
                            logger.info("成功注册TCP重连回调函数")
                        except Exception as e:
                            logger.error(f"注册TCP重连回调函数失败: {e}")
                    
                    # 更新UI状态
                    if hasattr(self, 'connect_button'):
                        self.connect_button.setText("断开")
                    if hasattr(self, 'comm_status_label'):
                        self.comm_status_label.setText("已连接")
                        self.comm_status_label.setStyleSheet("color: green; font-weight: bold;")
                    
                    # 显示连接成功消息
                    protocol_name = "串口" if comm_type == CommunicationType.SERIAL.value else "TCP"
                    QMessageBox.information(self, "成功", f"{protocol_name}连接成功")
                else:
                    QMessageBox.critical(self, "错误", "启动通信失败")
            else:
                # 断开连接
                if self.comm_manager.stop_communication():
                    if hasattr(self, 'connect_button'):
                        self.connect_button.setText("连接")
                    if hasattr(self, 'comm_status_label'):
                        self.comm_status_label.setText("未连接")
                        self.comm_status_label.setStyleSheet("color: red; font-weight: bold;")
                    
                    QMessageBox.information(self, "成功", "连接已断开")
                else:
                    QMessageBox.critical(self, "错误", "断开连接失败")
                    
        except Exception as e:
            logger.error(f"切换连接状态失败: {e}")
            QMessageBox.critical(self, "错误", f"连接操作失败: {e}")
    
    def test_connection(self):
        """测试连接 - 需要管理员权限"""
        try:
            # 检查权限
            if not self._check_permission(UserRole.ADMIN):
                return
            if self.comm_manager.test_connection():
                QMessageBox.information(self, "成功", "连接测试成功")
            else:
                QMessageBox.critical(self, "错误", "连接测试失败")
        except Exception as e:
            logger.error(f"测试连接失败: {e}")
            QMessageBox.critical(self, "错误", f"测试连接失败: {e}")
    
    def select_logo(self):
        """选择LOGO文件"""
        file_path, _ = QFileDialog.getOpenFileName(
            self, "选择医院LOGO", "", "图片文件 (*.png *.jpg *.jpeg *.bmp)"
        )
        if file_path and hasattr(self, 'logo_path_edit'):
            self.logo_path_edit.setText(file_path)
    
    def load_settings(self):
        """加载设置"""
        try:
            if hasattr(self, 'hospital_name_edit'):
                self.hospital_name_edit.setText(self.db_manager.get_system_config("hospital_name") or "")
            if hasattr(self, 'hospital_address_edit'):
                self.hospital_address_edit.setText(self.db_manager.get_system_config("hospital_address") or "")
            if hasattr(self, 'hospital_contact_edit'):
                self.hospital_contact_edit.setText(self.db_manager.get_system_config("hospital_contact") or "")
            if hasattr(self, 'logo_path_edit'):
                self.logo_path_edit.setText(self.db_manager.get_system_config("logo_path") or "")
            
            # 加载通信类型设置
            if hasattr(self, 'comm_type_combo'):
                comm_type = self.db_manager.get_system_config("communication_type") or CommunicationType.SERIAL.value
                self.comm_type_combo.setCurrentText(comm_type)
                # 触发通信类型改变事件
                self.on_comm_type_changed(comm_type)
            
            # 加载TCP设置
            if hasattr(self, 'ip_edit'):
                tcp_host = self.db_manager.get_system_config("tcp_host") or "127.0.0.1"
                self.ip_edit.setText(tcp_host)
            
            if hasattr(self, 'port_spin'):
                tcp_port = int(self.db_manager.get_system_config("tcp_port") or "5000")
                self.port_spin.setValue(tcp_port)
            
            # 加载串口设置
            if hasattr(self, 'serial_port_combo'):
                serial_port = self.db_manager.get_system_config("serial_port") or "COM3"
                self.serial_port_combo.setCurrentText(serial_port)
            
            if hasattr(self, 'baudrate_combo'):
                baudrate = self.db_manager.get_system_config("baudrate") or "115200"
                self.baudrate_combo.setCurrentText(baudrate)
            
            # 加载TCP服务器设置
            if hasattr(self, 'tcp_server_host_edit'):
                tcp_server_host = self.db_manager.get_system_config("tcp_server_host") or "0.0.0.0"
                self.tcp_server_host_edit.setText(tcp_server_host)
            
            if hasattr(self, 'tcp_server_port_spin'):
                tcp_server_port = int(self.db_manager.get_system_config("tcp_server_port") or "5001")
                self.tcp_server_port_spin.setValue(tcp_server_port)
            
            if hasattr(self, 'tcp_server_max_clients_spin'):
                tcp_server_max_clients = int(self.db_manager.get_system_config("tcp_server_max_clients") or "10")
                self.tcp_server_max_clients_spin.setValue(tcp_server_max_clients)
        except Exception as e:
            logger.error(f"加载设置失败: {e}")
    
    def load_warning_ranges(self):
        """加载警告区间配置"""
        try:
            if not hasattr(self, 'warning_table'):
                return
            
            # 从数据库加载警告区间配置
            warning_ranges = self.db_manager.get_warning_ranges()
            
            self.warning_table.setRowCount(len(warning_ranges))
            for row, warning_range in enumerate(warning_ranges):
                self.warning_table.setItem(row, 0, QTableWidgetItem(warning_range.test_name))
                self.warning_table.setItem(row, 1, QTableWidgetItem(str(warning_range.min_value)))
                self.warning_table.setItem(row, 2, QTableWidgetItem(str(warning_range.max_value)))
                self.warning_table.setItem(row, 3, QTableWidgetItem(warning_range.unit))
                self.warning_table.setItem(row, 4, QTableWidgetItem(warning_range.warning_color))
            
        except Exception as e:
            logger.error(f"加载警告区间失败: {e}")
    
    def add_warning_range(self):
        """添加警告区间"""
        try:
            from ui.dialogs import WarningRangeDialog
            dialog = WarningRangeDialog(self)
            if dialog.exec() == QDialog.DialogCode.Accepted:
                warning_range = dialog.get_warning_range()
                if self.db_manager.add_warning_range(warning_range):
                    self.load_warning_ranges()
                    QMessageBox.information(self, "成功", "警告区间添加成功")
                else:
                    QMessageBox.critical(self, "错误", "警告区间添加失败")
        except Exception as e:
            logger.error(f"添加警告区间失败: {e}")
            QMessageBox.critical(self, "错误", f"添加警告区间失败: {e}")
    
    def edit_warning_range(self):
        """编辑警告区间"""
        try:
            current_row = self.warning_table.currentRow()
            if current_row < 0:
                QMessageBox.warning(self, "警告", "请选择要编辑的警告区间")
                return
            
            # 获取当前选中的警告区间
            test_name = self.warning_table.item(current_row, 0).text()
            warning_ranges = self.db_manager.get_warning_ranges({"test_name": test_name})
            
            if warning_ranges:
                from ui.dialogs import WarningRangeDialog
                dialog = WarningRangeDialog(self, warning_ranges[0])
                if dialog.exec() == QDialog.DialogCode.Accepted:
                    updated_range = dialog.get_warning_range()
                    if self.db_manager.update_warning_range(updated_range):
                        self.load_warning_ranges()
                        QMessageBox.information(self, "成功", "警告区间更新成功")
                    else:
                        QMessageBox.critical(self, "错误", "警告区间更新失败")
            else:
                QMessageBox.warning(self, "警告", "未找到选中的警告区间")
        except Exception as e:
            logger.error(f"编辑警告区间失败: {e}")
            QMessageBox.critical(self, "错误", f"编辑警告区间失败: {e}")
    
    def delete_warning_range(self):
        """删除警告区间"""
        try:
            current_row = self.warning_table.currentRow()
            if current_row < 0:
                QMessageBox.warning(self, "警告", "请选择要删除的警告区间")
                return
            
            # 获取当前选中的警告区间
            test_name = self.warning_table.item(current_row, 0).text()
            
            reply = QMessageBox.question(
                self, "确认删除", f"确定要删除检测项目 '{test_name}' 的警告区间吗？",
                QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No
            )
            
            if reply == QMessageBox.StandardButton.Yes:
                if self.db_manager.delete_warning_range(test_name):
                    self.load_warning_ranges()
                    QMessageBox.information(self, "成功", "警告区间删除成功")
                else:
                    QMessageBox.critical(self, "错误", "警告区间删除失败")
        except Exception as e:
            logger.error(f"删除警告区间失败: {e}")
            QMessageBox.critical(self, "错误", f"删除警告区间失败: {e}")
    
    def save_settings(self):
        """保存设置"""
        try:
            if hasattr(self, 'hospital_name_edit'):
                self.db_manager.set_system_config("hospital_name", self.hospital_name_edit.text())
            if hasattr(self, 'hospital_address_edit'):
                self.db_manager.set_system_config("hospital_address", self.hospital_address_edit.text())
            if hasattr(self, 'hospital_contact_edit'):
                self.db_manager.set_system_config("hospital_contact", self.hospital_contact_edit.text())
            if hasattr(self, 'logo_path_edit'):
                self.db_manager.set_system_config("logo_path", self.logo_path_edit.text())
            
            # 保存通信类型设置
            if hasattr(self, 'comm_type_combo'):
                self.db_manager.set_system_config("communication_type", self.comm_type_combo.currentText())
            
            # 保存TCP设置
            if hasattr(self, 'ip_edit'):
                self.db_manager.set_system_config("tcp_host", self.ip_edit.text())
            
            if hasattr(self, 'port_spin'):
                self.db_manager.set_system_config("tcp_port", str(self.port_spin.value()))
            
            # 保存串口设置
            if hasattr(self, 'serial_port_combo'):
                self.db_manager.set_system_config("serial_port", self.serial_port_combo.currentText())
            
            if hasattr(self, 'baudrate_combo'):
                self.db_manager.set_system_config("baudrate", self.baudrate_combo.currentText())
            
            # 保存TCP服务器设置
            if hasattr(self, 'tcp_server_host_edit'):
                self.db_manager.set_system_config("tcp_server_host", self.tcp_server_host_edit.text())
            
            if hasattr(self, 'tcp_server_port_spin'):
                self.db_manager.set_system_config("tcp_server_port", str(self.tcp_server_port_spin.value()))
            
            if hasattr(self, 'tcp_server_max_clients_spin'):
                self.db_manager.set_system_config("tcp_server_max_clients", str(self.tcp_server_max_clients_spin.value()))
            
            # 记录操作日志
            if self.current_user:
                log = OperationLog(
                    id=0,
                    operation_time=datetime.now(),
                    operator=self.current_user.username,
                    operation_type="系统设置",
                    operation_content="更新系统设置",
                    ip_address="127.0.0.1"
                )
                self.db_manager.add_operation_log(log)
            
            # 更新备份检查点
            self.update_backup_checkpoint()
            
            QMessageBox.information(self, "成功", "设置已保存")
        except Exception as e:
            logger.error(f"保存设置失败: {e}")
            QMessageBox.critical(self, "错误", f"保存设置失败: {e}")
    
    def on_data_received(self, message: dict):
        """接收到数据 - 异步处理避免阻塞UI"""
        try:
            # logger.info(f"接收到解析后的消息: {message}")
            
            # 现在接收的是已经解析的消息字典，而不是原始字节数据
            if message:
                # 使用QTimer异步处理数据，避免阻塞UI线程
                QTimer.singleShot(0, lambda: self._process_received_data_async(message))
                        
        except Exception as e:
            logger.error(f"数据接收处理失败: {e}")
            # 更新状态栏显示
            if hasattr(self, 'status_bar'):
                self.status_bar.showMessage(f"❌ 数据接收失败: {str(e)}")
    
    def _process_received_data_async(self, message: dict):
        """异步处理接收到的数据"""
        try:
            # 获取原始数据（如果有的话）
            raw_data = message.get('raw', '')
            
            # 获取通信类型
            comm_type = self.comm_manager.get_communication_type()
            
            # 使用新的方法创建Sample对象，传递通信类型参数
            sample = self.data_processor.create_sample_from_parsed_data(message, raw_data, comm_type)
            
            logger.info(f"成功创建Sample对象: {sample.id}, {sample.sample_info.sample_number}, {sample.sample_info.patient_name}")
            
            # 保存样本到数据库
            if self.db_manager.add_sample(sample):
                operation_type = f"{comm_type}数据接收"
                
                # 记录操作日志
                if self.current_user:
                    log = OperationLog(
                        id=0,
                        operation_time=datetime.now(),
                        operator=self.current_user.username,
                        operation_type=operation_type,
                        operation_content=f"接收到样本数据: {sample.sample_info.sample_number}, 仪器: {sample.machine_model}, 协议: {sample.protocol}",
                        ip_address="127.0.0.1"
                    )
                    self.db_manager.add_operation_log(log)
                
                # 更新备份检查点
                self.update_backup_checkpoint()
                
                # 数据入库成功，记录日志
                logger.info(f"样本数据入库成功: {sample.sample_info.sample_number}")
                
                # 更新状态栏显示
                if hasattr(self, 'status_bar'):
                    self.status_bar.showMessage(f"✅ {comm_type}数据入库成功: {sample.sample_info.sample_number} | 仪器: {sample.machine_model} | 协议: {sample.protocol} | 时间: {sample.receive_time.strftime('%H:%M:%S')}")
                
                # 显示成功提示（可选）
                logger.info(f"✅ 成功处理并保存样本: {sample.sample_info.sample_number}, 协议: {sample.protocol}")
                
                # 强制设置批量传输状态（确保检测生效）
                self._force_set_batch_transfer_state()
                
                # 检测批量传输状态并管理刷新
                self._detect_batch_transfer()
                
                # 更新状态栏显示批量传输进度
                if hasattr(self, 'status_bar') and hasattr(self, '_batch_transfer_active') and self._batch_transfer_active:
                    self.status_bar.showMessage(f"📡 正在接收数据: {sample.sample_info.sample_number} | 协议: {sample.protocol} | 批量传输进行中...")
                
                # 如果当前在样本管理标签页，延迟刷新数据（添加防抖机制）
                if self.tab_widget.currentIndex() == 0:  # 样本管理标签页
                    # 取消之前的刷新定时器，避免重复刷新
                    if hasattr(self, '_refresh_timer') and self._refresh_timer is not None:
                        try:
                            self._refresh_timer.stop()
                        except Exception as e:
                            logger.debug(f"停止刷新定时器失败: {e}")
                    
                    # 创建新的刷新定时器，延迟3秒执行
                    self._refresh_timer = QTimer()
                    self._refresh_timer.setSingleShot(True)
                    self._refresh_timer.timeout.connect(self.refresh_after_new_sample)
                    self._refresh_timer.start(3000)  # 延迟3秒执行
                
                # 可选：显示入库成功提示
                # QMessageBox.information(self, "数据入库成功", f"样本 {sample.sample_info.sample_number} 已成功入库")
                
            else:
                logger.error(f"❌ 保存样本失败: {sample.sample_info.sample_number}")
                
                # 更新状态栏显示
                if hasattr(self, 'status_bar'):
                    self.status_bar.showMessage(f"❌ {comm_type}数据入库失败: {sample.sample_info.sample_number}")
                
        except Exception as e:
            logger.error(f"处理接收到的数据失败: {e}")
            # 更新状态栏显示
            if hasattr(self, 'status_bar'):
                self.status_bar.showMessage(f"❌ 数据处理失败: {str(e)}")
    
    def _detect_batch_transfer(self):
        """检测批量传输状态"""
        try:
            current_time = datetime.now()
            
            # 更新最后接收数据的时间
            if self._last_data_received_time is None:
                self._last_data_received_time = current_time
                self._batch_transfer_active = True
                self._batch_transfer_count = 1
                logger.debug("开始检测批量传输")
            else:
                # 计算距离上次接收数据的时间间隔
                time_diff = (current_time - self._last_data_received_time).total_seconds()
                
                # 如果时间间隔小于10秒，认为是批量传输（降低门槛）
                if time_diff < 10:
                    self._batch_transfer_active = True
                    self._last_data_received_time = current_time
                    self._batch_transfer_count += 1
                    
                    # 取消之前的批量传输完成检测定时器
                    if self._batch_transfer_timer is not None:
                        self._batch_transfer_timer.stop()
                    
                    # 创建新的批量传输完成检测定时器
                    self._batch_transfer_timer = QTimer()
                    self._batch_transfer_timer.setSingleShot(True)
                    self._batch_transfer_timer.timeout.connect(self._on_batch_transfer_complete)
                    self._batch_transfer_timer.start(6000)  # 6秒后检测批量传输是否完成（减少延迟）
                    
                    logger.debug(f"批量传输活跃，距离上次数据: {time_diff:.1f}秒，累计样本: {self._batch_transfer_count}")
                else:
                    # 如果时间间隔大于10秒，可能批量传输已结束
                    if self._batch_transfer_active:
                        logger.debug(f"批量传输可能已结束，距离上次数据: {time_diff:.1f}秒，总样本: {self._batch_transfer_count}")
                        self._batch_transfer_active = False
                        
        except Exception as e:
            logger.error(f"检测批量传输状态失败: {e}")
    
    def _force_set_batch_transfer_state(self):
        """强制设置批量传输状态，确保检测生效"""
        try:
            current_time = datetime.now()
            
            # 如果还没有开始批量传输，强制开始
            if not self._batch_transfer_active:
                self._batch_transfer_active = True
                self._last_data_received_time = current_time
                self._batch_transfer_count = 1
                logger.debug("强制开始批量传输检测")
                
                # 创建批量传输完成检测定时器
                if self._batch_transfer_timer is not None:
                    self._batch_transfer_timer.stop()
                
                self._batch_transfer_timer = QTimer()
                self._batch_transfer_timer.setSingleShot(True)
                self._batch_transfer_timer.timeout.connect(self._on_batch_transfer_complete)
                self._batch_transfer_timer.start(6000)  # 6秒后检测批量传输是否完成
                
            else:
                # 如果已经在批量传输中，更新时间和计数
                self._last_data_received_time = current_time
                self._batch_transfer_count += 1
                
                # 重置定时器
                if self._batch_transfer_timer is not None:
                    self._batch_transfer_timer.stop()
                    self._batch_transfer_timer.start(6000)
                
                logger.debug(f"批量传输状态更新，累计样本: {self._batch_transfer_count}")
                
        except Exception as e:
            logger.error(f"强制设置批量传输状态失败: {e}")
    
    def _on_batch_transfer_complete(self):
        """批量传输完成后的处理"""
        try:
            if self._batch_transfer_active:
                batch_count = self._batch_transfer_count
                self._batch_transfer_active = False
                self._batch_transfer_count = 0
                logger.info(f"批量传输完成，共处理 {batch_count} 个样本，执行最终刷新")
                
                # 批量传输完成后，执行一次强制刷新
                if self.tab_widget.currentIndex() == 0:  # 样本管理标签页
                    # 取消之前的刷新定时器
                    if self._refresh_timer is not None:
                        try:
                            self._refresh_timer.stop()
                        except Exception as e:
                            logger.debug(f"停止刷新定时器失败: {e}")
                    
                    # 执行强制刷新，确保获取最新数据
                    QTimer.singleShot(1000, self._force_refresh_after_batch_transfer)
                    
                    # 更新状态栏
                    if hasattr(self, 'status_bar'):
                        self.status_bar.showMessage("🔄 批量传输完成，正在强制刷新数据...")
                        
        except Exception as e:
            logger.error(f"批量传输完成处理失败: {e}")
    
    def _force_refresh_after_batch_transfer(self):
        """批量传输完成后的强制刷新"""
        try:
            logger.info("开始批量传输完成后的强制刷新...")
            
            # 检查当前是否在样本管理标签页
            if self.tab_widget.currentIndex() == 0:  # 样本管理标签页
                # 获取当前样本总数作为比较基准
                current_total = self.sample_pagination_manager.get_pagination_info().total_items
                logger.info(f"批量传输完成后强制刷新前样本总数: {current_total}")
                
                # 强制清除所有缓存，确保获取最新数据
                if hasattr(self, 'sample_pagination_manager'):
                    self.sample_pagination_manager.clear_cache()
                    logger.info("批量传输完成后：已清除分页管理器缓存")
                
                if hasattr(self, 'lazy_data_loader'):
                    self.lazy_data_loader.clear_cache()
                    logger.info("批量传输完成后：已清除数据加载器缓存")
                
                # 检查是否有搜索条件
                search_text = self.search_edit.text().strip() if hasattr(self, 'search_edit') else ""
                
                if search_text:
                    # 如果有搜索条件，执行搜索刷新
                    logger.info("批量传输完成后：执行搜索刷新")
                    self.search_samples()
                else:
                    # 如果没有搜索条件，执行普通刷新
                    logger.info("批量传输完成后：执行普通刷新")
                    self.load_samples()
                
                # 获取刷新后的样本总数
                new_total = self.sample_pagination_manager.get_pagination_info().total_items
                logger.info(f"批量传输完成后强制刷新后样本总数: {new_total}")
                
                # 检测数据变化
                data_change = new_total - current_total
                
                # 更新状态栏显示
                if hasattr(self, 'status_bar'):
                    pagination_info = self.sample_pagination_manager.get_pagination_info()
                    
                    if data_change > 0:
                        change_info = f"🆕 批量传输完成，发现 {data_change} 个新样本"
                    elif data_change < 0:
                        change_info = f"🗑️ 批量传输完成，减少 {abs(data_change)} 个样本"
                    else:
                        change_info = "📊 批量传输完成，数据无变化"
                    
                    status_text = f"✅ 批量传输完成 - {change_info} | {pagination_info.total_items} 个样本，第 {pagination_info.current_page} 页 | 刷新时间: {QTime.currentTime().toString('HH:%M:%S')}"
                    self.status_bar.showMessage(status_text)
                
                logger.info(f"批量传输完成后强制刷新完成，变化: {data_change}")
                
                # 如果数据没有变化，记录警告
                if data_change == 0:
                    logger.warning("批量传输完成后强制刷新仍无变化，可能存在数据同步问题")
            else:
                logger.debug("当前不在样本管理标签页，跳过批量传输完成后的强制刷新")
                
        except Exception as e:
            logger.error(f"批量传输完成后强制刷新失败: {e}")
            if hasattr(self, 'status_bar'):
                self.status_bar.showMessage(f"❌ 批量传输完成后强制刷新失败: {str(e)}")
    
    def update_status(self):
        """更新状态"""
        try:
            if hasattr(self, 'comm_status_label'):
                if self.comm_manager.is_connected:
                    self.comm_status_label.setText("已连接")
                    self.comm_status_label.setStyleSheet("color: green; font-weight: bold;")
                else:
                    self.comm_status_label.setText("未连接")
                    self.comm_status_label.setStyleSheet("color: red; font-weight: bold;")
        except Exception as e:
            logger.error(f"更新状态失败: {e}")
    
    def update_batch_transfer_status(self):
        """更新批量传输状态"""
        try:
            if hasattr(self, 'status_bar') and hasattr(self, '_batch_transfer_active'):
                if self._batch_transfer_active:
                    # 计算距离上次接收数据的时间
                    if self._last_data_received_time is not None:
                        time_diff = (datetime.now() - self._last_data_received_time).total_seconds()
                        if time_diff < 15:  # 15秒内显示活跃状态（增加容忍度）
                            self.status_bar.showMessage(f"🔄 批量传输进行中... 最后数据: {time_diff:.1f}秒前，累计样本: {self._batch_transfer_count}")
                        else:
                            # 超过15秒，可能传输已完成，自动触发完成检测
                            logger.debug(f"批量传输状态超时({time_diff:.1f}秒)，自动触发完成检测")
                            self._on_batch_transfer_complete()
                            
        except Exception as e:
            logger.error(f"更新批量传输状态失败: {e}")
    
    def quick_copy_sample(self):
        """快速复制样本 - 需要管理员权限"""
        try:
            # 检查权限
            if not self._check_permission(UserRole.ADMIN):
                return
                
            if not hasattr(self, 'sample_table'):
                QMessageBox.warning(self, "警告", "样本表格未初始化")
                return
                
            current_row = self.sample_table.currentRow()
            if current_row < 0:
                QMessageBox.warning(self, "警告", "请选择要复制的样本")
                return
            
            # 使用新的方法获取样本数据 - 通过隐藏的ID列
            original_sample = self.get_sample_by_row(current_row)
            if not original_sample:
                QMessageBox.warning(self, "警告", "无法获取选中的样本数据")
                return
            
            # 创建新的样本对象（复制数据但生成新的ID）
            original_sample_number = getattr(original_sample.sample_info, 'sample_number', 'UNKNOWN')
            new_sample = Sample(
                id=f"COPY_{datetime.now().strftime('%Y%m%d%H%M%S')}",
                protocol=original_sample.protocol,
                machine_model=original_sample.machine_model,
                machine_id=original_sample.machine_id,
                sample_info=original_sample.sample_info,
                results=original_sample.results,
                image=original_sample.image,
                analysis_mode=original_sample.analysis_mode,
                mode_info=original_sample.mode_info,
                raw=original_sample.raw,
                receive_time=datetime.now(),
                status=SampleStatus.PENDING,
                created_time=datetime.now(),
                updated_time=datetime.now()
            )
            
            # 添加新样本到数据库
            if self.db_manager.add_sample(new_sample):
                # 记录操作日志
                new_sample_number = getattr(new_sample.sample_info, 'sample_number', 'UNKNOWN')
                patient_name = getattr(new_sample.sample_info, 'patient_name', '未知患者')
                log = OperationLog(
                    id=0,
                    operation_time=datetime.now(),
                    operator=self.current_user.username if self.current_user else "未知用户",
                    operation_type="快速复制样本",
                    operation_content=f"复制样本: {original_sample_number} -> {new_sample_number}, 患者: {patient_name}",
                    ip_address="127.0.0.1"  # 实际应用中应获取真实IP
                )
                self.db_manager.add_operation_log(log)
                
                # 更新备份检查点
                self.update_backup_checkpoint()
                
                # 刷新样本列表
                # 🔧 修复：使用直接刷新方法
                self._direct_refresh_samples()
                QMessageBox.information(self, "成功", f"样本复制成功: {new_sample_number}")
            else:
                QMessageBox.critical(self, "错误", "样本复制失败")
        except Exception as e:
            logger.error(f"快速复制样本失败: {e}")
            QMessageBox.critical(self, "错误", f"快速复制样本失败: {e}")
    
    def view_log_details(self):
        """查看日志详情"""
        try:
            if not hasattr(self, 'logs_table'):
                QMessageBox.warning(self, "警告", "日志表格未初始化")
                return
                
            current_row = self.logs_table.currentRow()
            if current_row < 0:
                QMessageBox.warning(self, "警告", "请选择要查看的日志")
                return
            
            # 获取日志数据
            try:
                log_id = int(self.logs_table.item(current_row, 0).text())
            except (ValueError, AttributeError) as e:
                logger.error(f"获取日志ID失败: {e}")
                QMessageBox.critical(self, "错误", "获取日志ID失败，请重新选择日志")
                return
                
            logs = self.db_manager.get_operation_logs({"id": str(log_id)})
            
            if logs:
                try:
                    from ui.dialogs import LogDetailsDialog
                    dialog = LogDetailsDialog(logs[0], self)
                    dialog.exec()
                except Exception as e:
                    logger.error(f"创建日志详情对话框失败: {e}")
                    QMessageBox.critical(self, "错误", f"显示日志详情失败: {e}")
            else:
                QMessageBox.warning(self, "警告", "未找到选中的日志")
        except Exception as e:
            logger.error(f"查看日志详情失败: {e}")
            QMessageBox.critical(self, "错误", f"查看日志详情失败: {e}")
    

    # 用户管理相关方法
    def load_users(self):
        """加载用户数据"""
        try:
            if not hasattr(self, 'users_table'):
                logger.warning("用户表格控件未初始化")
                return
                
            users = self.db_manager.get_all_users()
            self.users_table.setRowCount(len(users))
            
            for row, user in enumerate(users):
                # 选择框
                checkbox = QTableWidgetItem()
                checkbox.setFlags(Qt.ItemFlag.ItemIsUserCheckable | Qt.ItemFlag.ItemIsEnabled)
                checkbox.setCheckState(Qt.CheckState.Unchecked)
                self.users_table.setItem(row, 0, checkbox)
                
                # 用户信息
                self.users_table.setItem(row, 1, QTableWidgetItem(str(user.id)))
                self.users_table.setItem(row, 2, QTableWidgetItem(user.username))
                self.users_table.setItem(row, 3, QTableWidgetItem(user.role.value))
                self.users_table.setItem(row, 4, QTableWidgetItem(user.created_time.strftime("%Y-%m-%d %H:%M:%S")))
                
                # 最后登录时间
                last_login = user.last_login.strftime("%Y-%m-%d %H:%M:%S") if user.last_login else "从未登录"
                self.users_table.setItem(row, 5, QTableWidgetItem(last_login))
            
            if hasattr(self, 'status_bar'):
                self.status_bar.showMessage(f"已加载 {len(users)} 个用户")
            
        except Exception as e:
            logger.error(f"加载用户失败: {e}")
            QMessageBox.critical(self, "错误", f"加载用户失败: {e}")
    
    def add_user(self):
        """新增用户 - 需要管理员权限"""
        try:
            # 检查权限
            if not self._check_permission(UserRole.ADMIN):
                return
            dialog = UserEditDialog(self)
            if dialog.exec() == QDialog.DialogCode.Accepted:
                user_data = dialog.get_user_data()
                
                if self.db_manager.add_user(user_data["username"], user_data["password"], user_data["role"]):
                    # 记录操作日志
                    log = OperationLog(
                        id=0,
                        operation_time=datetime.now(),
                        operator=self.current_user.username if self.current_user else "未知用户",
                        operation_type="新增用户",
                        operation_content=f"新增用户: {user_data['username']}, 角色: {user_data['role'].value}",
                        ip_address="127.0.0.1"
                    )
                    self.db_manager.add_operation_log(log)
                    
                    # 更新备份检查点
                    self.update_backup_checkpoint()
                    
                    # 刷新用户列表
                    self.load_users()
                    QMessageBox.information(self, "成功", f"用户 {user_data['username']} 添加成功")
                else:
                    QMessageBox.critical(self, "错误", "用户添加失败，请检查用户名是否已存在")
        except Exception as e:
            logger.error(f"新增用户失败: {e}")
            QMessageBox.critical(self, "错误", f"新增用户失败: {e}")
    
    def edit_user(self):
        """编辑用户 - 需要管理员权限"""
        try:
            # 检查权限
            if not self._check_permission(UserRole.ADMIN):
                return
            current_row = self.users_table.currentRow()
            if current_row < 0:
                QMessageBox.warning(self, "警告", "请选择要编辑的用户")
                return
            
            # 获取当前选中的用户
            user_id = int(self.users_table.item(current_row, 1).text())
            user = self.db_manager.get_user_by_id(user_id)
            
            if user:
                dialog = UserEditDialog(self, user)
                if dialog.exec() == QDialog.DialogCode.Accepted:
                    user_data = dialog.get_user_data()
                    
                    if self.db_manager.update_user(user_id, user_data["username"], user_data["role"], user_data["password"]):
                        # 记录操作日志
                        log = OperationLog(
                            id=0,
                            operation_time=datetime.now(),
                            operator=self.current_user.username if self.current_user else "未知用户",
                            operation_type="编辑用户",
                            operation_content=f"编辑用户: {user_data['username']}, 角色: {user_data['role'].value}",
                            ip_address="127.0.0.1"
                        )
                        self.db_manager.add_operation_log(log)
                        
                        # 更新备份检查点
                        self.update_backup_checkpoint()
                        
                        # 刷新用户列表
                        self.load_users()
                        QMessageBox.information(self, "成功", f"用户 {user_data['username']} 更新成功")
                    else:
                        QMessageBox.critical(self, "错误", "用户更新失败，请检查用户名是否已存在")
            else:
                QMessageBox.warning(self, "警告", "未找到选中的用户")
        except Exception as e:
            logger.error(f"编辑用户失败: {e}")
            QMessageBox.critical(self, "错误", f"编辑用户失败: {e}")
    
    def delete_user(self):
        """删除用户 - 需要管理员权限"""
        try:
            # 检查权限
            if not self._check_permission(UserRole.ADMIN):
                return
            current_row = self.users_table.currentRow()
            if current_row < 0:
                QMessageBox.warning(self, "警告", "请选择要删除的用户")
                return
            
            # 获取当前选中的用户
            user_id = int(self.users_table.item(current_row, 1).text())
            username = self.users_table.item(current_row, 2).text()
            
            reply = QMessageBox.question(
                self, "确认删除", f"确定要删除用户 '{username}' 吗？",
                QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No
            )
            
            if reply == QMessageBox.StandardButton.Yes:
                if self.db_manager.delete_user(user_id):
                    # 记录操作日志
                    log = OperationLog(
                        id=0,
                        operation_time=datetime.now(),
                        operator=self.current_user.username if self.current_user else "未知用户",
                        operation_type="删除用户",
                        operation_content=f"删除用户: {username}",
                        ip_address="127.0.0.1"
                    )
                    self.db_manager.add_operation_log(log)
                    
                    # 更新备份检查点
                    self.update_backup_checkpoint()
                    
                    # 刷新用户列表
                    self.load_users()
                    QMessageBox.information(self, "成功", f"用户 {username} 删除成功")
                else:
                    QMessageBox.critical(self, "错误", "用户删除失败，可能是最后一个管理员用户")
        except Exception as e:
            logger.error(f"删除用户失败: {e}")
            QMessageBox.critical(self, "错误", f"删除用户失败: {e}")
    
    def batch_delete_users(self):
        """批量删除用户 - 需要管理员权限"""
        try:
            # 检查权限
            if not self._check_permission(UserRole.ADMIN):
                return
            selected_rows = set()
            for item in self.users_table.selectedItems():
                selected_rows.add(item.row())
            
            if not selected_rows:
                QMessageBox.warning(self, "警告", "请选择要删除的用户")
                return
            
            # 获取选中的用户信息
            selected_users = []
            for row in selected_rows:
                user_id = int(self.users_table.item(row, 1).text())
                username = self.users_table.item(row, 2).text()
                selected_users.append((user_id, username))
            
            # 确认删除
            usernames = [username for _, username in selected_users]
            reply = QMessageBox.question(
                self, "确认批量删除", f"确定要删除以下用户吗？\n{', '.join(usernames)}",
                QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No
            )
            
            if reply == QMessageBox.StandardButton.Yes:
                user_ids = [user_id for user_id, _ in selected_users]
                if self.db_manager.delete_users(user_ids):
                    # 记录操作日志
                    log = OperationLog(
                        id=0,
                        operation_time=datetime.now(),
                        operator=self.current_user.username if self.current_user else "未知用户",
                        operation_type="批量删除用户",
                        operation_content=f"批量删除用户: {', '.join(usernames)}",
                        ip_address="127.0.0.1"
                    )
                    self.db_manager.add_operation_log(log)
                    
                    # 更新备份检查点
                    self.update_backup_checkpoint()
                    
                    # 刷新用户列表
                    self.load_users()
                    QMessageBox.information(self, "成功", f"成功删除 {len(selected_users)} 个用户")
                else:
                    QMessageBox.critical(self, "错误", "批量删除失败，可能是要删除所有管理员用户")
        except Exception as e:
            logger.error(f"批量删除用户失败: {e}")
            QMessageBox.critical(self, "错误", f"批量删除用户失败: {e}")
    
    def change_user_password(self):
        """修改用户密码 - 需要管理员权限"""
        try:
            # 检查权限
            if not self._check_permission(UserRole.ADMIN):
                return
            current_row = self.users_table.currentRow()
            if current_row < 0:
                QMessageBox.warning(self, "警告", "请选择要修改密码的用户")
                return
            
            # 获取当前选中的用户
            user_id = int(self.users_table.item(current_row, 1).text())
            username = self.users_table.item(current_row, 2).text()
            
            dialog = UserPasswordDialog(self, username)
            if dialog.exec() == QDialog.DialogCode.Accepted:
                new_password = dialog.get_new_password()
                
                if self.db_manager.change_user_password(user_id, new_password):
                    # 记录操作日志
                    log = OperationLog(
                        id=0,
                        operation_time=datetime.now(),
                        operator=self.current_user.username if self.current_user else "未知用户",
                        operation_type="修改密码",
                        operation_content=f"修改用户密码: {username}",
                        ip_address="127.0.0.1"
                    )
                    self.db_manager.add_operation_log(log)
                    
                    # 更新备份检查点
                    self.update_backup_checkpoint()
                    
                    QMessageBox.information(self, "成功", f"用户 {username} 密码修改成功")
                else:
                    QMessageBox.critical(self, "错误", "密码修改失败")
        except Exception as e:
            logger.error(f"修改用户密码失败: {e}")
            QMessageBox.critical(self, "错误", f"修改用户密码失败: {e}")
    
    def manage_user_permissions(self):
        """管理用户权限 - 需要管理员权限"""
        try:
            # 检查权限
            if not self._check_permission(UserRole.ADMIN):
                return
            current_row = self.users_table.currentRow()
            if current_row < 0:
                QMessageBox.warning(self, "警告", "请选择要管理权限的用户")
                return
            
            # 获取当前选中的用户
            user_id = int(self.users_table.item(current_row, 1).text())
            user = self.db_manager.get_user_by_id(user_id)
            
            if user:
                dialog = UserPermissionDialog(self, user)
                if dialog.exec() == QDialog.DialogCode.Accepted:
                    permissions = dialog.get_permissions()
                    # 这里可以保存权限设置到数据库
                    QMessageBox.information(self, "成功", f"用户 {user.username} 权限设置已保存")
            else:
                QMessageBox.warning(self, "警告", "未找到选中的用户")
        except Exception as e:
            logger.error(f"管理用户权限失败: {e}")
            QMessageBox.critical(self, "错误", f"管理用户权限失败: {e}")
    
    def search_users(self):
        """搜索用户"""
        try:
            if not hasattr(self, 'user_search_edit') or not hasattr(self, 'users_table'):
                logger.warning("用户搜索控件未初始化")
                return
                
            search_text = self.user_search_edit.text().strip()
            if not search_text:
                self.load_users()
                return
            
            users = self.db_manager.search_users(search_text)
            self.users_table.setRowCount(len(users))
            
            for row, user in enumerate(users):
                # 选择框
                checkbox = QTableWidgetItem()
                checkbox.setFlags(Qt.ItemFlag.ItemIsUserCheckable | Qt.ItemFlag.ItemIsEnabled)
                checkbox.setCheckState(Qt.CheckState.Unchecked)
                self.users_table.setItem(row, 0, checkbox)
                
                # 用户信息
                self.users_table.setItem(row, 1, QTableWidgetItem(str(user.id)))
                self.users_table.setItem(row, 2, QTableWidgetItem(user.username))
                self.users_table.setItem(row, 3, QTableWidgetItem(user.role.value))
                self.users_table.setItem(row, 4, QTableWidgetItem(user.created_time.strftime("%Y-%m-%d %H:%M:%S")))
                
                # 最后登录时间
                last_login = user.last_login.strftime("%Y-%m-%d %H:%M:%S") if user.last_login else "从未登录"
                self.users_table.setItem(row, 5, QTableWidgetItem(last_login))
            
            if hasattr(self, 'status_bar'):
                self.status_bar.showMessage(f"搜索到 {len(users)} 个用户")
            
        except Exception as e:
            logger.error(f"搜索用户失败: {e}")
    
    def on_user_selection_changed(self):
        """用户选择变化事件"""
        try:
            selected_rows = self.users_table.selectionModel().selectedRows()
            has_selection = len(selected_rows) > 0
            
            # 根据选择状态启用/禁用按钮
            if hasattr(self, 'edit_user_button'):
                self.edit_user_button.setEnabled(has_selection)
            if hasattr(self, 'delete_user_button'):
                self.delete_user_button.setEnabled(has_selection)
            if hasattr(self, 'change_password_button'):
                self.change_password_button.setEnabled(has_selection)
            if hasattr(self, 'permission_button'):
                self.permission_button.setEnabled(has_selection)
            
            # 批量删除按钮需要多个选择
            if hasattr(self, 'batch_delete_users_button'):
                self.batch_delete_users_button.setEnabled(len(selected_rows) > 1)
                
        except Exception as e:
            logger.error(f"用户选择变化事件处理失败: {e}")
    
    # 备份相关方法
    
    def open_backup_settings(self):
        """打开备份设置对话框"""
        try:
            # 使用绝对导入，避免相对导入问题
            try:
                from src.ui.backup_dialog import BackupDialog
                from src.core.backup_manager import BackupManager
            except ImportError:
                # 如果绝对导入失败，尝试相对导入
                try:
                    from .backup_dialog import BackupDialog
                    from ..core.backup_manager import BackupManager
                except ImportError:
                    QMessageBox.critical(self, "错误", "无法导入备份相关模块")
                    return
            
            # 获取配置文件路径
            config_path = None
            if hasattr(self, 'config') and hasattr(self.config, 'config_file'):
                config_path = self.config.config_file
            else:
                # 如果没有config属性，使用默认路径
                project_root = Path(__file__).parent.parent.parent
                config_path = str(project_root / "data" / "config.json")
            
            # 创建备份管理器
            backup_manager = BackupManager(
                db_path=self.db_manager.db_path,
                config_path=config_path
            )
            
            # 打开备份设置对话框
            dialog = BackupDialog(backup_manager, self)
            dialog.exec()
            
            # 刷新备份状态
            self.update_backup_status()
            
        except Exception as e:
            logger.error(f"打开备份设置失败: {e}")
            QMessageBox.critical(self, "错误", f"打开备份设置失败: {str(e)}")
    
    def create_backup_now(self):
        """立即创建备份"""
        try:
            # 使用绝对导入，避免相对导入问题
            try:
                from src.core.backup_manager import BackupManager
            except ImportError:
                try:
                    from ..core.backup_manager import BackupManager
                except ImportError:
                    QMessageBox.critical(self, "错误", "无法导入备份管理器模块")
                    return
            
            # 获取配置文件路径
            config_path = None
            if hasattr(self, 'config') and hasattr(self.config, 'config_file'):
                config_path = self.config.config_file
            else:
                # 如果没有config属性，使用默认路径
                project_root = Path(__file__).parent.parent.parent
                config_path = str(project_root / "data" / "config.json")
            
            # 创建备份管理器
            backup_manager = BackupManager(
                db_path=self.db_manager.db_path,
                config_path=config_path
            )
            
            # 记录手动备份开始日志
            if self.system_logger:
                self.system_logger.log_system_event(
                    "手动备份开始",
                    "用户从主窗口手动创建备份",
                    details={
                        "db_path": self.db_manager.db_path,
                        "config_path": config_path
                    }
                )
            
            # 创建备份
            # 使用时间戳作为文件名
            timestamp = datetime.now().strftime("%Y%m%d%H%M%S")
            backup_name = f"{timestamp}_manual_backup"
            backup_path = backup_manager.create_backup(backup_name)
            
            if backup_path:
                # 记录手动备份成功日志
                if self.system_logger:
                    backup_size = os.path.getsize(backup_path) if os.path.exists(backup_path) else 0
                    self.system_logger.log_system_event(
                        "手动备份完成",
                        f"手动备份创建成功: {os.path.basename(backup_path)}",
                        details={
                            "backup_path": backup_path,
                            "backup_size": backup_size,
                            "backup_name": os.path.basename(backup_path)
                        }
                    )
                
                QMessageBox.information(self, "成功", f"备份创建成功: {backup_path}")
                
                # 记录操作日志
                self.log_operation("创建备份", f"手动创建备份: {backup_path}")
                
                # 刷新备份状态
                self.update_backup_status()
            else:
                # 记录手动备份失败日志
                if self.system_logger:
                    self.system_logger.log_system_event(
                        "手动备份失败",
                        "手动备份创建失败",
                        log_level=LogLevel.ERROR,
                        details={
                            "db_path": self.db_manager.db_path,
                            "config_path": config_path,
                            "error": "备份创建返回None"
                        }
                    )
                
                QMessageBox.critical(self, "错误", "备份创建失败")
                
        except Exception as e:
            logger.error(f"创建备份失败: {e}")
            
            # 记录手动备份失败日志
            if self.system_logger:
                self.system_logger.log_system_event(
                    "手动备份失败",
                    f"手动备份创建失败: {str(e)}",
                    log_level=LogLevel.ERROR,
                    details={
                        "db_path": self.db_manager.db_path,
                        "error": str(e),
                        "error_type": type(e).__name__
                    }
                )
            
            QMessageBox.critical(self, "错误", f"创建备份失败: {str(e)}")
    
    def restore_backup(self):
        """恢复备份"""
        try:
            # 使用绝对导入，避免相对导入问题
            try:
                from src.core.backup_manager import BackupManager
            except ImportError:
                try:
                    from ..core.backup_manager import BackupManager
                except ImportError:
                    QMessageBox.critical(self, "错误", "无法导入备份管理器模块")
                    return
            
            # 创建备份管理器
            backup_manager = BackupManager(
                db_path=self.db_manager.db_path,
                config_path=self.config.config_file if hasattr(self, 'config') else str(Path(__file__).parent.parent.parent / "data" / "config.json")
            )
            
            # 选择备份文件
            backup_file, _ = QFileDialog.getOpenFileName(
                self, "选择备份文件", "",
                "备份文件 (*.zip *.zip.encrypted);;所有文件 (*.*)"
            )
            
            if backup_file:
                # 记录恢复备份开始日志
                if self.system_logger:
                    backup_name = os.path.basename(backup_file) if backup_file else "未知"
                    self.system_logger.log_system_event(
                        "备份恢复开始",
                        f"用户从主窗口开始恢复备份: {backup_name}",
                        details={
                            "backup_file": backup_file,
                            "backup_name": backup_name
                        }
                    )
                
                reply = QMessageBox.question(
                    self, "确认恢复", 
                    "确定要恢复这个备份吗？这将覆盖当前的数据。",
                    QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No
                )
                
                if reply == QMessageBox.StandardButton.Yes:
                    # 恢复备份
                    success = backup_manager.restore_backup(backup_file)
                    
                    if success:
                        # 记录恢复备份成功日志
                        if self.system_logger:
                            backup_name = os.path.basename(backup_file) if backup_file else "未知"
                            self.system_logger.log_system_event(
                                "备份恢复完成",
                                f"备份恢复成功: {backup_name}",
                                details={
                                    "backup_file": backup_file,
                                    "backup_name": backup_name,
                                    "restore_result": "success"
                                }
                            )
                        
                        QMessageBox.information(self, "成功", "备份恢复成功，需要重启应用程序")
                        
                        # 记录操作日志
                        self.log_operation("恢复备份", f"恢复备份: {backup_file}")
                        
                        # 这里可以发送重启信号或直接退出
                        self.close()
                    else:
                        # 记录恢复备份失败日志
                        if self.system_logger:
                            backup_name = os.path.basename(backup_file) if backup_file else "未知"
                            self.system_logger.log_system_event(
                                "备份恢复失败",
                                f"备份恢复失败: {backup_name}",
                                log_level=LogLevel.ERROR,
                                details={
                                    "backup_file": backup_file,
                                    "backup_name": backup_name,
                                    "restore_result": "failed"
                                }
                            )
                        
                        QMessageBox.critical(self, "错误", "备份恢复失败")
                        
        except Exception as e:
            logger.error(f"恢复备份失败: {e}")
            
            # 记录恢复备份失败日志
            if self.system_logger:
                self.system_logger.log_system_event(
                    "备份恢复失败",
                    f"恢复备份异常: {str(e)}",
                    log_level=LogLevel.ERROR,
                    details={
                        "error": str(e),
                        "error_type": type(e).__name__
                    }
                )
            
            QMessageBox.critical(self, "错误", f"恢复备份失败: {str(e)}")
    
    def update_backup_status(self):
        """更新备份状态显示"""
        try:
            # 使用绝对导入，避免相对导入问题
            try:
                from src.core.backup_manager import BackupManager
            except ImportError:
                try:
                    from ..core.backup_manager import BackupManager
                except ImportError:
                    logger.error("无法导入备份管理器模块")
                    self.backup_status_label.setText("模块导入失败")
                    return
            
            # 创建备份管理器
            backup_manager = BackupManager(
                db_path=self.db_manager.db_path,
                config_path=self.config.config_file if hasattr(self, 'config') else None
            )
            
            # 获取备份配置
            config = backup_manager.backup_config
            
            # 更新状态标签
            if config["auto_backup"]:
                self.backup_status_label.setText("已启用")
                self.backup_status_label.setStyleSheet("color: green; font-weight: bold;")
            else:
                self.backup_status_label.setText("已禁用")
                self.backup_status_label.setStyleSheet("color: red; font-weight: bold;")
            
            # 更新备份路径
            backup_path = str(backup_manager.backup_path)
            self.backup_path_label.setText(backup_path)
            
            # 获取最后备份时间
            backup_list = backup_manager.get_backup_list()
            if backup_list:
                last_backup = backup_list[0]["modified_time"]
                try:
                    last_time = datetime.fromisoformat(last_backup)
                    self.last_backup_label.setText(last_time.strftime("%Y-%m-%d %H:%M:%S"))
                except:
                    self.last_backup_label.setText("时间格式错误")
            else:
                self.last_backup_label.setText("从未备份")
                
        except Exception as e:
            logger.error(f"更新备份状态失败: {e}")
            self.backup_status_label.setText("状态获取失败")
            self.backup_status_label.setStyleSheet("color: red; font-weight: bold;")
    
    def log_operation(self, operation_type: str, operation_content: str):
        """记录操作日志"""
        try:
            if self.current_user:
                log = OperationLog(
                    id=0,
                    operation_time=datetime.now(),
                    operator=self.current_user.username,
                    operation_type=operation_type,
                    operation_content=operation_content,
                    ip_address="127.0.0.1"  # 本地操作
                )
                
                self.db_manager.add_operation_log(log)
        
        except Exception as e:
            logger.error(f"记录操作日志失败: {e}")
    
    def closeEvent(self, event):
        """窗口关闭事件"""
        try:
            logger.info("程序正在关闭，开始清理资源...")
            
            # 检查是否在批量传输中，如果是则询问用户是否强制退出
            if hasattr(self, '_batch_transfer_active') and self._batch_transfer_active:
                reply = QMessageBox.question(
                    self, 
                    "批量传输进行中", 
                    "检测到批量传输正在进行中，强制退出可能会丢失数据。\n\n是否继续退出？",
                    QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No,
                    QMessageBox.StandardButton.No
                )
                
                if reply == QMessageBox.StandardButton.No:
                    event.ignore()
                    return
            
            # 停止所有定时器，避免后台任务继续运行
            self._stop_all_timers()
            
            # 停止通信管理器
            if hasattr(self, 'comm_manager'):
                try:
                    self.comm_manager.disconnect()
                    logger.info("通信管理器已断开连接")
                except Exception as e:
                    logger.error(f"断开通信管理器失败: {e}")
            
            # 执行程序关闭备份（异步执行，避免阻塞关闭）
            self._execute_close_backup_async()
            
            # 记录操作日志
            self.log_operation("程序关闭", "用户关闭程序")
            
            logger.info("程序关闭清理完成")
            
        except Exception as e:
            logger.error(f"程序关闭清理失败: {e}")
        
        # 立即接受关闭事件，避免阻塞
        event.accept()
    
    def _stop_all_timers(self):
        """停止所有定时器，避免后台任务继续运行"""
        try:
            # 停止状态更新定时器
            if hasattr(self, 'status_timer') and self.status_timer:
                self.status_timer.stop()
                logger.debug("状态更新定时器已停止")
            
            # 停止TCP状态更新定时器
            if hasattr(self, 'tcp_status_timer') and self.tcp_status_timer:
                self.tcp_status_timer.stop()
                logger.debug("TCP状态更新定时器已停止")
            
            # 停止批量传输状态定时器
            if hasattr(self, 'batch_status_timer') and self.batch_status_timer:
                self.batch_status_timer.stop()
                logger.debug("批量传输状态定时器已停止")
            
            # 停止刷新定时器
            if hasattr(self, '_refresh_timer') and self._refresh_timer:
                try:
                    self._refresh_timer.stop()
                    logger.debug("刷新定时器已停止")
                except Exception as e:
                    logger.debug(f"停止刷新定时器失败: {e}")
            
            # 停止批量传输完成检测定时器
            if hasattr(self, '_batch_transfer_timer') and self._batch_transfer_timer:
                try:
                    self._batch_transfer_timer.stop()
                    logger.debug("批量传输完成检测定时器已停止")
                except Exception as e:
                    logger.debug(f"停止批量传输完成检测定时器失败: {e}")
            
            # 停止备份超时定时器
            if hasattr(self, '_backup_timeout_timer') and self._backup_timeout_timer:
                try:
                    self._backup_timeout_timer.stop()
                    logger.debug("备份超时定时器已停止")
                except Exception as e:
                    logger.debug(f"停止备份超时定时器失败: {e}")
            
            logger.info("所有定时器已停止")
            
        except Exception as e:
            logger.error(f"停止定时器失败: {e}")
    
    def _execute_close_backup_async(self):
        """异步执行程序关闭备份，避免阻塞程序关闭"""
        try:
            # 使用QTimer.singleShot异步执行备份，不阻塞UI线程
            # 设置较短的延迟，避免程序关闭时等待太久
            QTimer.singleShot(100, self._perform_close_backup)
            
            # 设置备份超时定时器，防止备份卡死
            backup_timeout_timer = QTimer()
            backup_timeout_timer.setSingleShot(True)
            backup_timeout_timer.timeout.connect(self._on_backup_timeout)
            backup_timeout_timer.start(10000)  # 10秒超时
            
            # 保存超时定时器引用
            self._backup_timeout_timer = backup_timeout_timer
            
        except Exception as e:
            logger.error(f"启动异步备份失败: {e}")
    
    def _on_backup_timeout(self):
        """备份超时处理"""
        try:
            logger.warning("程序关闭备份超时，强制退出")
            # 强制退出程序
            QApplication.quit()
        except Exception as e:
            logger.error(f"备份超时处理失败: {e}")
    
    def _perform_close_backup(self):
        """执行程序关闭备份"""
        try:
            logger.info("=== 开始执行程序关闭备份 ===")
            
            # 检查是否启用了关闭备份
            if not hasattr(self, 'backup_manager') or not self.backup_manager:
                logger.info("备份管理器未初始化，跳过关闭备份")
                return
            
            # 检查备份配置
            if not self.backup_manager.backup_config.get("close_backup", True):
                logger.info("程序关闭备份已禁用，跳过关闭备份")
                return
            
            # 检查是否在批量传输中，如果是则跳过关闭备份
            if hasattr(self, '_batch_transfer_active') and self._batch_transfer_active:
                logger.info("批量传输进行中，跳过关闭备份避免干扰")
                return
            
            # 获取当前备份状态
            backup_status = self.backup_manager.get_backup_status()
            logger.info(f"当前备份状态: {backup_status}")
            
            # 检查是否已经创建了任何类型的备份，避免重复备份
            if backup_status.get("close_backup_created", False) or backup_status.get("emergency_backup_created", False):
                logger.info("备份已创建，跳过重复备份")
                return
            
            logger.info("所有检查通过，开始执行程序关闭备份...")
            
            # 执行关闭备份
            self.backup_manager.close_backup()
            
            logger.info("程序关闭备份完成")
            
        except Exception as e:
            logger.error(f"执行程序关闭备份失败: {e}")
        
        logger.info("=== 程序关闭备份执行结束 ===")
    
    def on_sample_selection_changed(self):
        """样本选择变化时更新预览和选中行样式 - 使用隐藏的ID列作为数据库索引"""
        try:
            # 更新选中行的字体颜色
            self.update_selected_row_style()
            
            current_row = self.sample_table.currentRow()
            if current_row < 0:
                self.clear_preview()
                return
            
            # 使用新的方法获取样本数据 - 通过隐藏的ID列
            sample = self.get_sample_by_row(current_row)
            if not sample:
                self.clear_preview()
                return
            
            self.update_preview(sample)
            
        except Exception as e:
            logger.error(f"更新样本预览失败: {e}")
            self.clear_preview()
    
    def update_preview(self, sample: Sample):
        """更新预览区域显示"""
        try:
            # 更新基本信息 - 使用新的数据结构
            self.preview_sample_number.setText(sample.sample_info.sample_number)
            self.preview_sample_uid.setText(sample.sample_info.sample_uid)    # 仪器检测编号
            self.preview_patient_name.setText(sample.sample_info.patient_name)
            self.preview_patient_age.setText(str(sample.sample_info.patient_age))
            self.preview_patient_gender.setText(sample.sample_info.patient_gender)
            self.preview_position.setText(sample.sample_info.position)
            self.preview_sample_type.setText(sample.sample_info.sample_type_name)
            self.preview_analysis_time.setText(sample.receive_time.strftime("%Y-%m-%d %H:%M:%S"))
            self.preview_receive_time.setText(sample.created_time.strftime("%Y-%m-%d %H:%M:%S"))
            self.preview_status.setText(sample.status.value)
            
            # 更新仪器信息 - 使用新的数据结构
            self.preview_instrument_model.setText(sample.machine_model)
            self.preview_instrument_number.setText(sample.machine_id)
            self.preview_analysis_mode.setText(sample.analysis_mode if sample.analysis_mode else "未知")
            self.preview_protocol_type.setText(sample.protocol)
            
            # 更新百分比结果预览
            percentage_text = self.format_percentage_results(sample.results)
            self.preview_percentage_results.setText(percentage_text)
            
            # 更新TIME数据预览
            time_text = self.format_time_data(sample.results)
            self.preview_time_data.setText(time_text)
            
            # 更新AREA数据预览
            area_text = self.format_area_data(sample.results)
            self.preview_area_data.setText(area_text)
            
            # 更新图片预览
            self.update_image_preview(sample.image.path)
            
        except Exception as e:
            logger.error(f"更新预览显示失败: {e}")
    
    def clear_preview(self):
        """清空预览区域"""
        try:
            # 清空基本信息
            self.preview_sample_number.setText("")
            self.preview_sample_uid.setText("")    # 仪器检测编号
            self.preview_patient_name.setText("")
            self.preview_patient_age.setText("")
            self.preview_patient_gender.setText("")
            self.preview_position.setText("")
            self.preview_sample_type.setText("")
            self.preview_analysis_time.setText("")
            self.preview_receive_time.setText("")
            self.preview_status.setText("")
            
            # 清空仪器信息
            self.preview_instrument_model.setText("")
            self.preview_instrument_number.setText("")
            self.preview_analysis_mode.setText("")
            self.preview_protocol_type.setText("")
            
            # 清空检测结果预览
            self.preview_percentage_results.setText("")
            self.preview_time_data.setText("")
            self.preview_area_data.setText("")
            
            # 清空图片预览
            self.preview_image_label.setText("暂无图片")
            self.preview_image_label.setPixmap(QPixmap())
            self.view_image_button.setEnabled(False)
            self.save_image_button.setEnabled(False)
            
        except Exception as e:
            logger.error(f"清空预览失败: {e}")
    
    def update_image_preview(self, image_path: str):
        """更新图片预览"""
        try:
            if not image_path or not os.path.exists(image_path):
                self.preview_image_label.setText("暂无图片")
                self.preview_image_label.setPixmap(QPixmap())
                self.view_image_button.setEnabled(False)
                self.save_image_button.setEnabled(False)
                return
            
            # 加载图片
            pixmap = QPixmap(image_path)
            if pixmap.isNull():
                self.preview_image_label.setText("图片加载失败")
                self.preview_image_label.setPixmap(QPixmap())
                self.view_image_button.setEnabled(False)
                self.save_image_button.setEnabled(False)
                return
            
            # 缩放图片以适应预览区域
            scaled_pixmap = pixmap.scaled(
                300, 200, 
                Qt.AspectRatioMode.KeepAspectRatio, 
                Qt.TransformationMode.SmoothTransformation
            )
            
            self.preview_image_label.setPixmap(scaled_pixmap)
            self.preview_image_label.setText("")
            self.view_image_button.setEnabled(True)
            self.save_image_button.setEnabled(True)
            
            # 保存当前图片路径供后续操作使用
            self.current_preview_image_path = image_path
            
        except Exception as e:
            logger.error(f"更新图片预览失败: {e}")
            self.preview_image_label.setText("图片加载失败")
            self.preview_image_label.setPixmap(QPixmap())
            self.view_image_button.setEnabled(False)
            self.save_image_button.setEnabled(False)
    
    def view_preview_image(self):
        """查看预览图片的大图"""
        try:
            if hasattr(self, 'current_preview_image_path') and self.current_preview_image_path:
                self.view_image(self.current_preview_image_path)
        except Exception as e:
            logger.error(f"查看预览图片失败: {e}")
            QMessageBox.critical(self, "错误", f"查看图片失败: {e}")
    
    def save_preview_image(self):
        """保存预览图片"""
        try:
            if not hasattr(self, 'current_preview_image_path') or not self.current_preview_image_path:
                QMessageBox.warning(self, "警告", "没有可保存的图片")
                return
            
            if not os.path.exists(self.current_preview_image_path):
                QMessageBox.warning(self, "警告", "图片文件不存在")
                return
            
            # 获取保存路径
            file_path, _ = QFileDialog.getSaveFileName(
                self, "保存图片", 
                os.path.basename(self.current_preview_image_path),
                "图片文件 (*.png *.jpg *.jpeg *.bmp)"
            )
            
            if file_path:
                import shutil
                shutil.copy2(self.current_preview_image_path, file_path)
                QMessageBox.information(self, "成功", f"图片已保存到: {file_path}")
                
        except Exception as e:
            logger.error(f"保存预览图片失败: {e}")
            QMessageBox.critical(self, "错误", f"保存图片失败: {e}")
    
    def on_item_double_clicked(self, item):
        """处理表格项双击事件"""
        try:
            row = item.row()
            col = item.column()
            
            # 只处理第11、12、13列（百分比结果、TIME数据、AREA数据）- 已调整列索引
            if col in [11, 12, 13]:
                # 使用更可靠的方法获取样本数据
                sample = self.get_sample_by_row(row)
                if not sample:
                    return
                
                # 根据列号获取对应的完整数据
                if col == 11:  # 百分比结果
                    title = "百分比结果详情"
                    content = self.format_percentage_results(sample.results)
                elif col == 12:  # TIME数据
                    title = "TIME数据详情（出峰时间）"
                    content = self.format_time_data(sample.results)
                else:  # AREA数据
                    title = "AREA数据详情（峰面积）"
                    content = self.format_area_data(sample.results)
                
                # 显示详情对话框
                self.show_data_details_dialog(title, content, sample)
                
        except Exception as e:
            logger.error(f"处理双击事件失败: {e}")
    
    def show_data_details_dialog(self, title: str, content: str, sample: Sample):
        """显示数据详情对话框"""
        try:
            from PyQt6.QtWidgets import QDialog, QVBoxLayout, QHBoxLayout, QLabel, QTextEdit, QPushButton, QGroupBox, QFormLayout
            
            dialog = QDialog(self)
            dialog.setWindowTitle(title)
            dialog.setModal(True)
            dialog.resize(600, 500)
            
            layout = QVBoxLayout(dialog)
            
            # 样本基本信息
            info_group = QGroupBox("样本信息")
            info_layout = QFormLayout(info_group)
            
            info_layout.addRow("样本编号:", QLabel(sample.sample_info.sample_number))
            info_layout.addRow("患者姓名:", QLabel(sample.sample_info.patient_name))
            info_layout.addRow("设备分析时间:", QLabel(sample.receive_time.strftime("%Y-%m-%d %H:%M:%S")))
            info_layout.addRow("仪器型号:", QLabel(sample.machine_model))
            
            layout.addWidget(info_group)
            
            # 数据内容
            content_group = QGroupBox("详细内容")
            content_layout = QVBoxLayout(content_group)
            
            content_text = QTextEdit()
            content_text.setPlainText(content)
            content_text.setReadOnly(True)
            content_text.setStyleSheet("""
                QTextEdit {
                    font-family: Consolas, Monaco, monospace;
                    font-size: 10px;
                    background-color: #f8f9fa;
                    border: 1px solid #dee2e6;
                }
            """)
            content_layout.addWidget(content_text)
            
            layout.addWidget(content_group)
            
            # 按钮
            button_layout = QHBoxLayout()
            
            copy_button = QPushButton("复制内容")
            copy_button.clicked.connect(lambda: self.copy_to_clipboard(content))
            button_layout.addWidget(copy_button)
            
            button_layout.addStretch()
            
            close_button = QPushButton("关闭")
            close_button.clicked.connect(dialog.accept)
            button_layout.addWidget(close_button)
            
            layout.addLayout(button_layout)
            
            dialog.exec()
            
        except Exception as e:
            logger.error(f"显示数据详情对话框失败: {e}")
            QMessageBox.critical(self, "错误", f"显示数据详情失败: {e}")
    
    def copy_to_clipboard(self, text: str):
        """复制文本到剪贴板"""
        try:
            from PyQt6.QtWidgets import QApplication
            QApplication.clipboard().setText(text)
            QMessageBox.information(self, "成功", "内容已复制到剪贴板")
        except Exception as e:
            logger.error(f"复制到剪贴板失败: {e}")
            QMessageBox.critical(self, "错误", f"复制失败: {e}")
    
    def open_backup_manager(self):
        """打开备份管理器"""
        try:
            from .backup_dialog import BackupDialog
            self.backup_dialog = BackupDialog(self)
            self.backup_dialog.show()
            logger.info("打开备份管理器")
        except Exception as e:
            logger.error(f"打开备份管理器失败: {e}")
            QMessageBox.critical(self, "错误", f"打开备份管理器失败: {e}")
    
    def show_about(self):
        """显示关于对话框"""
        try:
            from PyQt6.QtWidgets import QDialog, QVBoxLayout, QLabel, QPushButton, QHBoxLayout
            
            dialog = QDialog(self)
            dialog.setWindowTitle("关于")
            dialog.setModal(True)
            dialog.resize(400, 300)
            
            layout = QVBoxLayout(dialog)
            
            # 标题
            title_label = QLabel("小苏科技-样本管理系统")
            title_label.setStyleSheet("font-size: 18px; font-weight: bold; margin: 10px;")
            title_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
            layout.addWidget(title_label)
            
            # 版本信息
            version_label = QLabel("版本: 1.0.0")
            version_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
            layout.addWidget(version_label)
            
            # 功能说明
            features_text = """
            功能特性:
            • 样本管理 - 完整的样本生命周期管理
            • 数据通信 - 支持多种仪器通信协议
            • 数据备份 - 自动备份和恢复
            • 用户管理 - 多角色权限控制
            • 系统日志 - 完整的操作记录
            • 样本DIY - 可对样本进行DIY编辑和打印

            """
            
            features_label = QLabel(features_text)
            features_label.setStyleSheet("margin: 10px;")
            layout.addWidget(features_label)
            
            # 版权信息
            copyright_label = QLabel("© 2025 小苏科技. 保留所有权利.")
            copyright_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
            layout.addWidget(copyright_label)
            
            # 按钮
            button_layout = QHBoxLayout()
            button_layout.addStretch()
            
            ok_button = QPushButton("确定")
            ok_button.clicked.connect(dialog.accept)
            button_layout.addWidget(ok_button)
            
            layout.addLayout(button_layout)
            
            dialog.exec()
            
        except Exception as e:
            logger.error(f"显示关于对话框失败: {e}")
            QMessageBox.critical(self, "错误", f"显示关于对话框失败: {e}")
    
    def start_tcp_server_if_enabled(self):
        """启动TCP服务器（如果启用）"""
        try:
            # 延迟启动，等待UI初始化完成
            QTimer.singleShot(1000, self._start_tcp_server_if_enabled)
        except Exception as e:
            logger.error(f"启动TCP服务器失败: {e}")
    
    def _start_tcp_server_if_enabled(self):
        """实际启动TCP服务器（如果启用）"""
        try:
            # 移除自动启动逻辑，改为手动启动
            pass
        except Exception as e:
            logger.error(f"启动TCP服务器失败: {e}")
    
    def toggle_tcp_server(self):
        """切换TCP服务器状态"""
        try:
            if not self.comm_manager.is_tcp_server_running():
                # 启动TCP服务器
                host = self.tcp_server_host_edit.text()
                port = self.tcp_server_port_spin.value()
                max_clients = self.tcp_server_max_clients_spin.value()
                
                # 设置TCP服务器
                if self.comm_manager.setup_tcp_server(host, port, max_clients):
                    # 设置样本推送服务的TCP服务器
                    self.sample_push_service.set_tcp_server(self.comm_manager.tcp_server)
                    
                    # 启动服务器
                    if self.comm_manager.start_tcp_server():
                        self.tcp_server_connect_button.setText("断开")
                        self.tcp_server_status_label.setText("已连接")
                        self.tcp_server_status_label.setStyleSheet("color: green; font-weight: bold;")
                        logger.info(f"TCP服务器启动成功: {host}:{port}")
                    else:
                        QMessageBox.critical(self, "错误", "TCP服务器启动失败")
                else:
                    QMessageBox.critical(self, "错误", "TCP服务器设置失败")
            else:
                # 停止TCP服务器
                self.comm_manager.stop_tcp_server()
                self.tcp_server_connect_button.setText("连接")
                self.tcp_server_status_label.setText("未连接")
                self.tcp_server_status_label.setStyleSheet("color: red; font-weight: bold;")
                self.tcp_server_client_count_label.setText("0")
                logger.info("TCP服务器已停止")
        except Exception as e:
            logger.error(f"切换TCP服务器状态失败: {e}")
            QMessageBox.critical(self, "错误", f"TCP服务器操作失败: {e}")
    
    def test_tcp_server(self):
        """测试TCP服务器连接"""
        try:
            if not self.comm_manager.is_tcp_server_running():
                QMessageBox.warning(self, "警告", "TCP服务器未运行")
                return
            
            client_count = self.comm_manager.get_tcp_server_client_count()
            client_list = self.comm_manager.get_tcp_server_client_list()
            
            if client_count > 0:
                client_info = "\n".join(client_list)
                QMessageBox.information(self, "连接测试", f"TCP服务器运行正常\n连接客户端数: {client_count}\n客户端列表:\n{client_info}")
            else:
                QMessageBox.information(self, "连接测试", "TCP服务器运行正常，但当前没有客户端连接")
                
        except Exception as e:
            logger.error(f"测试TCP服务器失败: {e}")
            QMessageBox.critical(self, "错误", f"测试TCP服务器失败: {e}")
    
    def update_tcp_server_status(self):
        """更新TCP服务器状态"""
        try:
            if hasattr(self, 'tcp_server_status_label') and hasattr(self, 'tcp_server_client_count_label'):
                if self.comm_manager.is_tcp_server_running():
                    client_count = self.comm_manager.get_tcp_server_client_count()
                    self.tcp_server_client_count_label.setText(str(client_count))
                    if client_count > 0:
                        self.tcp_server_status_label.setText(f"已连接 ({client_count})")
                        self.tcp_server_status_label.setStyleSheet("color: green; font-weight: bold;")
                    else:
                        self.tcp_server_status_label.setText("已连接 (无客户端)")
                        self.tcp_server_status_label.setStyleSheet("color: orange; font-weight: bold;")
                else:
                    self.tcp_server_status_label.setText("未连接")
                    self.tcp_server_status_label.setStyleSheet("color: red; font-weight: bold;")
                    self.tcp_server_client_count_label.setText("0")
        except Exception as e:
            logger.error(f"更新TCP服务器状态失败: {e}")
    
    # 排序相关方法
    
    def on_sort_field_changed(self, field_name: str):
        """排序字段改变"""
        try:
            logger.info(f"排序字段改变为: {field_name}")
            # 可以在这里添加一些逻辑，比如自动应用排序
        except Exception as e:
            logger.error(f"排序字段改变处理失败: {e}")
    
    def on_sort_order_changed(self, order_name: str):
        """排序方式改变"""
        try:
            logger.info(f"排序方式改变为: {order_name}")
            # 可以在这里添加一些逻辑，比如自动应用排序
        except Exception as e:
            logger.error(f"排序方式改变处理失败: {e}")
    
    def apply_sorting(self):
        """应用排序"""
        try:
            # 获取当前排序设置
            sort_field = "已入库时间"  # 默认按已入库时间排序
            sort_order = "最新优先"  # 默认最新优先
            
            logger.info(f"应用排序: 字段={sort_field}, 方式={sort_order}")
            
            # 重新加载样本数据并应用排序
            self.load_samples_with_sorting(sort_field, sort_order)
            
            # 显示排序成功提示
            if hasattr(self, 'status_bar'):
                self.status_bar.showMessage(f"排序已应用: {sort_field} - {sort_order}")
                
        except Exception as e:
            logger.error(f"应用排序失败: {e}")
            QMessageBox.critical(self, "错误", f"应用排序失败: {e}")
    
    def load_samples_with_sorting(self, sort_field: str, sort_order: str):
        """加载样本数据并应用排序 - 使用优化的分页加载"""
        try:
            if not hasattr(self, 'sample_table'):
                logger.warning("样本表格控件未初始化")
                return
            
            # 显示加载状态
            if hasattr(self, 'status_bar'):
                self.status_bar.showMessage("正在应用排序并加载数据...")
            
            # 设置排序方式
            from src.utils.optimized_pagination_manager import SortOrder
            sort_order_enum = SortOrder.DESC if sort_order == "最新优先" else SortOrder.ASC
            
            # 更新分页管理器的排序设置
            self.sample_pagination_manager.set_sorting(sort_field, sort_order_enum)
            
            # 重置到第一页并加载数据
            self.sample_pagination_manager.go_to_page(1)
            
            # 获取当前页数据并填充表格
            current_samples = self.sample_pagination_manager.get_current_data()
            self.fill_sample_table(current_samples)
            
            # 自动选择第一行并显示预览
            if len(current_samples) > 0:
                self.sample_table.selectRow(0)
                self.on_sample_selection_changed()
            
            # 更新分页控件
            if hasattr(self, 'sample_pagination_widget'):
                self.sample_pagination_widget.update_ui()
            
            # 更新状态栏
            if hasattr(self, 'status_bar'):
                pagination_info = self.sample_pagination_manager.get_pagination_info()
                self.status_bar.showMessage(f"排序已应用: {sort_field} - {sort_order}，共 {pagination_info.total_items} 个样本")
            
            logger.info(f"样本排序完成: 字段={sort_field}, 方式={sort_order}, 样本数={pagination_info.total_items}")
                
        except Exception as e:
            logger.error(f"加载排序样本失败: {e}")
            QMessageBox.critical(self, "错误", f"加载排序样本失败: {e}")
            if hasattr(self, 'status_bar'):
                self.status_bar.showMessage("样本排序失败")
    
    def sort_samples(self, samples: list, sort_field: str, sort_order: str) -> list:
        """对样本列表进行排序"""
        try:
            if not samples:
                return []
            
            # 定义排序字段映射
            field_mapping = {
                "接收时间": "created_time",
                "已入库时间": "created_at",
                "样本编号": "sample_info.sample_number",
                "患者姓名": "sample_info.patient_name",
                "年龄": "sample_info.patient_age",
                "性别": "sample_info.patient_gender",
                "仪器型号": "machine_model",
                "仪器编号": "machine_id",
                "位置": "sample_info.position",
                "状态": "status",
                "分析模式": "analysis_mode",
                "协议": "protocol"
            }
            
            # 获取排序字段，默认按接收时间排序
            sort_key = field_mapping.get(sort_field, "created_time")
            
            # 定义排序函数
            def get_sort_value(sample):
                try:
                    if sort_key == "receive_time":
                        return sample.receive_time
                    elif sort_key == "created_time":
                        return sample.created_time
                    elif sort_key == "sample_info.sample_number":
                        return sample.sample_info.sample_number or ""
                    elif sort_key == "sample_info.patient_name":
                        return sample.sample_info.patient_name or ""
                    elif sort_key == "sample_info.patient_age":
                        return sample.sample_info.patient_age or 0
                    elif sort_key == "sample_info.patient_gender":
                        return sample.sample_info.patient_gender or ""
                    elif sort_key == "machine_model":
                        return sample.machine_model or ""
                    elif sort_key == "machine_id":
                        return sample.machine_id or ""
                    elif sort_key == "sample_info.position":
                        return sample.sample_info.position or ""
                    elif sort_key == "status":
                        return sample.status.value if sample.status else ""
                    elif sort_key == "analysis_mode":
                        return sample.analysis_mode or ""
                    elif sort_key == "protocol":
                        return sample.protocol or ""
                    else:
                        return ""
                except Exception as e:
                    logger.warning(f"获取排序值失败: {e}")
                    return ""
            
            # 执行排序
            reverse = (sort_order == "最新优先")
            
            # 特殊处理时间字段的排序
            if sort_key in ["receive_time", "created_time"]:
                # 时间字段，最新优先时降序，最老优先时升序
                sorted_samples = sorted(samples, key=get_sort_value, reverse=reverse)
            else:
                # 其他字段，最新优先时降序，最老优先时升序
                sorted_samples = sorted(samples, key=get_sort_value, reverse=reverse)
            
            logger.info(f"样本排序完成: 字段={sort_field}, 方式={sort_order}, 样本数={len(sorted_samples)}")
            return sorted_samples
            
        except Exception as e:
            logger.error(f"样本排序失败: {e}")
            return samples  # 排序失败时返回原列表
    
    def fill_sample_table(self, samples: list):
        """填充样本表格"""
        try:
            if not hasattr(self, 'sample_table'):
                return
            
            self.sample_table.setRowCount(len(samples))
            
            for row, sample in enumerate(samples):
                # ID列 - 完全隐藏列，存储样本的唯一ID，用于数据库索引
                id_item = QTableWidgetItem(sample.id)
                id_item.setData(Qt.ItemDataRole.UserRole, sample.id)
                self.sample_table.setItem(row, 0, id_item)
                
                # 基本信息
                sample_number_item = QTableWidgetItem(sample.sample_info.sample_number)
                sample_number_item.setData(Qt.ItemDataRole.UserRole, sample.id)
                self.sample_table.setItem(row, 1, sample_number_item)
                
                # 仪器检测编号
                self.sample_table.setItem(row, 2, QTableWidgetItem(sample.sample_info.sample_uid))
                
                self.sample_table.setItem(row, 3, QTableWidgetItem(sample.sample_info.patient_name))
                self.sample_table.setItem(row, 4, QTableWidgetItem(str(sample.sample_info.patient_age)))
                self.sample_table.setItem(row, 5, QTableWidgetItem(sample.sample_info.patient_gender))
                self.sample_table.setItem(row, 6, QTableWidgetItem(sample.machine_model))
                self.sample_table.setItem(row, 7, QTableWidgetItem(sample.machine_id))
                self.sample_table.setItem(row, 8, QTableWidgetItem(sample.sample_info.position))
                
                # 设备分析时间列 - 安全处理时间字段
                try:
                    if hasattr(sample.receive_time, 'strftime'):
                        receive_time_str = sample.receive_time.strftime("%Y-%m-%d %H:%M:%S")
                    else:
                        receive_time_str = str(sample.receive_time) if sample.receive_time else "未知"
                    self.sample_table.setItem(row, 9, QTableWidgetItem(receive_time_str))
                except Exception:
                    self.sample_table.setItem(row, 9, QTableWidgetItem(str(sample.receive_time) if sample.receive_time else "未知"))
                
                # 接收时间列 - 安全处理时间字段
                try:
                    if hasattr(sample.created_time, 'strftime'):
                        created_time_str = sample.created_time.strftime("%Y-%m-%d %H:%M:%S")
                    else:
                        created_time_str = str(sample.created_time) if sample.created_time else "未知"
                    self.sample_table.setItem(row, 10, QTableWidgetItem(created_time_str))
                except Exception:
                    self.sample_table.setItem(row, 10, QTableWidgetItem(str(sample.created_time) if sample.created_time else "未知"))
                
                # 状态列
                status_item = QTableWidgetItem(sample.status.value)
                if sample.status == SampleStatus.PENDING:
                    status_item.setBackground(Qt.GlobalColor.yellow)
                elif sample.status == SampleStatus.PROCESSED:
                    status_item.setBackground(Qt.GlobalColor.green)
                self.sample_table.setItem(row, 11, status_item)
                
                # 百分比结果
                percentage_text = self.format_percentage_results(sample.results)
                display_text = percentage_text[:100] + "..." if len(percentage_text) > 100 else percentage_text
                percentage_item = QTableWidgetItem(display_text)
                percentage_item.setToolTip(percentage_text)
                self.sample_table.setItem(row, 12, percentage_item)
                
                # TIME数据
                time_text = self.format_time_data(sample.results)
                display_text = time_text[:100] + "..." if len(time_text) > 100 else time_text
                time_item = QTableWidgetItem(display_text)
                time_item.setToolTip(time_text)
                self.sample_table.setItem(row, 13, time_item)
                
                # AREA数据
                area_text = self.format_area_data(sample.results)
                display_text = area_text[:100] + "..." if len(area_text) > 100 else area_text
                area_item = QTableWidgetItem(display_text)
                area_item.setToolTip(area_text)
                self.sample_table.setItem(row, 14, area_item)
                
                # 分析模式
                mode_display = sample.analysis_mode if sample.analysis_mode else "未知"
                self.sample_table.setItem(row, 15, QTableWidgetItem(mode_display))
                
                # 图片列
                if sample.image.path and os.path.exists(sample.image.path):
                    image_button = QPushButton("查看")
                    # 修复lambda闭包问题，使用functools.partial
                    from functools import partial
                    image_button.clicked.connect(partial(self.view_image, sample.image.path))
                    self.sample_table.setCellWidget(row, 16, image_button)
                
                # 协议类型
                self.sample_table.setItem(row, 17, QTableWidgetItem(sample.protocol))
                

                
        except Exception as e:
            logger.error(f"填充样本表格失败: {e}")
    
    def get_sorting_info(self) -> dict:
        """获取当前排序信息"""
        try:
            return {
                "field": "接收时间",
                "order": "最新优先"
            }
        except Exception as e:
            logger.error(f"获取排序信息失败: {e}")
            return {"field": "接收时间", "order": "最新优先"}
    
    def set_sorting_info(self, field: str, order: str):
        """设置排序信息"""
        try:
            # 由于排序控件被注释掉了，这里暂时不执行任何操作
            pass
        except Exception as e:
            logger.error(f"设置排序信息失败: {e}")
    
    def fill_logs_table(self, logs: list):
        """填充日志表格"""
        try:
            if not hasattr(self, 'logs_table'):
                return
            
            self.logs_table.setRowCount(len(logs))
            
            for row, log in enumerate(logs):
                self.logs_table.setItem(row, 0, QTableWidgetItem(str(log.id)))
                self.logs_table.setItem(row, 1, QTableWidgetItem(log.operation_time.strftime("%Y-%m-%d %H:%M:%S")))
                self.logs_table.setItem(row, 2, QTableWidgetItem(log.operator))
                self.logs_table.setItem(row, 3, QTableWidgetItem(log.operation_type))
                
                # 截断操作内容显示
                content = log.operation_content
                if len(content) > 100:
                    content = content[:100] + "..."
                self.logs_table.setItem(row, 4, QTableWidgetItem(content))
                self.logs_table.setItem(row, 5, QTableWidgetItem(log.ip_address))
                
        except Exception as e:
            logger.error(f"填充日志表格失败: {e}")
    
    def on_sample_page_changed(self, page: int):
        """样本分页改变事件"""
        try:
            # 获取当前页数据并填充表格
            current_samples = self.sample_pagination_manager.get_current_data()
            self.fill_sample_table(current_samples)
            
            # 自动选择第一行并显示预览
            if len(current_samples) > 0:
                self.sample_table.selectRow(0)
                self.on_sample_selection_changed()
            
            # 更新状态栏
            if hasattr(self, 'status_bar'):
                pagination_info = self.sample_pagination_manager.get_pagination_info()
                self.status_bar.showMessage(f"样本管理 - 第 {pagination_info.current_page} 页，共 {pagination_info.total_pages} 页")
                
        except Exception as e:
            logger.error(f"样本分页改变处理失败: {e}")
    
    def on_sample_page_size_changed(self, page_size: int):
        """样本每页大小改变事件"""
        try:
            # 获取当前页数据并填充表格
            current_samples = self.sample_pagination_manager.get_current_data()
            self.fill_sample_table(current_samples)
            
            # 自动选择第一行并显示预览
            if len(current_samples) > 0:
                self.sample_table.selectRow(0)
                self.on_sample_selection_changed()
            
            # 更新状态栏
            if hasattr(self, 'status_bar'):
                pagination_info = self.sample_pagination_manager.get_pagination_info()
                self.status_bar.showMessage(f"样本管理 - 每页 {page_size} 条，共 {pagination_info.total_items} 条")
                
        except Exception as e:
            logger.error(f"样本每页大小改变处理失败: {e}")
    
    def on_logs_page_changed(self, page: int):
        """日志分页改变事件"""
        try:
            # 获取当前页数据并填充表格
            current_logs = self.logs_pagination_manager.get_current_data()
            self.fill_logs_table(current_logs)
            
            # 更新状态栏
            if hasattr(self, 'status_bar'):
                pagination_info = self.logs_pagination_manager.get_pagination_info()
                self.status_bar.showMessage(f"系统日志 - 第 {pagination_info.current_page} 页，共 {pagination_info.total_pages} 页")
                
        except Exception as e:
            logger.error(f"日志分页改变处理失败: {e}")
    
    def on_logs_page_size_changed(self, page_size: int):
        """日志每页大小改变事件"""
        try:
            # 获取当前页数据并填充表格
            current_logs = self.logs_pagination_manager.get_current_data()
            self.fill_logs_table(current_logs)
            
            # 更新状态栏
            if hasattr(self, 'status_bar'):
                pagination_info = self.logs_pagination_manager.get_pagination_info()
                self.status_bar.showMessage(f"系统日志 - 每页 {page_size} 条，共 {pagination_info.total_items} 条")
                
        except Exception as e:
            logger.error(f"日志每页大小改变处理失败: {e}")
    
    def search_samples(self):
        """搜索样本（简单搜索）"""
        try:
            if not hasattr(self, 'search_edit') or not hasattr(self, 'sample_table'):
                logger.warning("搜索控件未初始化")
                return
                
            search_text = self.search_edit.text().strip()
            if not search_text:
                # 如果搜索框为空，重新加载所有样本
                self.load_samples()
                return
            
            # 使用分页管理器的搜索功能
            if self.sample_pagination_manager.search_samples(search_text):
                # 获取当前页数据并填充表格
                current_samples = self.sample_pagination_manager.get_current_data()
                self.fill_sample_table(current_samples)
                
                # 更新分页控件
                if hasattr(self, 'sample_pagination_widget'):
                    self.sample_pagination_widget.update_ui()
                
                # 自动选择第一行并显示预览
                if len(current_samples) > 0:
                    self.sample_table.selectRow(0)
                    self.on_sample_selection_changed()
                
                # 更新状态栏
                if hasattr(self, 'status_bar'):
                    pagination_info = self.sample_pagination_manager.get_pagination_info()
                    self.status_bar.showMessage(f"搜索到 {pagination_info.total_items} 个样本，当前显示第 {pagination_info.current_page} 页")
                
                logger.info(f"搜索成功: 找到 {pagination_info.total_items} 个样本")
            else:
                logger.warning("搜索失败")
                if hasattr(self, 'status_bar'):
                    self.status_bar.showMessage("搜索失败，请重试")
            
        except Exception as e:
            logger.error(f"搜索样本失败: {e}")
            QMessageBox.critical(self, "错误", f"搜索样本失败: {e}")
    
    def search_logs(self):
        """搜索日志"""
        try:
            if not hasattr(self, 'log_search_edit') or not hasattr(self, 'logs_table'):
                logger.warning("日志搜索控件未初始化")
                return
                
            search_text = self.log_search_edit.text().strip()
            if not search_text:
                self.load_logs()
                return
            
            filters = {
                "operator": search_text,
                "operation_type": search_text,
                "operation_content": search_text
            }
            logs = self.db_manager.get_operation_logs(filters)
            logger.info(f"搜索日志，找到 {len(logs)} 条匹配记录")
            
            # 设置过滤后的数据到分页管理器
            self.logs_pagination_manager.set_filtered_data(logs)
            
            # 获取当前页数据并填充表格
            current_logs = self.logs_pagination_manager.get_current_data()
            logger.info(f"当前页显示 {len(current_logs)} 条日志")
            self.fill_logs_table(current_logs)
            
            # 更新分页控件
            if hasattr(self, 'logs_pagination_widget'):
                self.logs_pagination_widget.update_ui()
            
            if hasattr(self, 'status_bar'):
                pagination_info = self.logs_pagination_manager.get_pagination_info()
                self.status_bar.showMessage(f"搜索到 {pagination_info.total_items} 条日志，当前显示第 {pagination_info.current_page} 页")
            
        except Exception as e:
            logger.error(f"搜索日志失败: {e}")
            QMessageBox.critical(self, "错误", f"搜索日志失败: {e}")
    
    def update_ui_permissions(self):
        """根据用户权限更新UI状态"""
        try:
            if not self.current_user:
                return
            
            role = self.current_user.role
            
            # 样本管理权限 - 查看员只能查看，不能编辑
            if role == UserRole.VIEWER:
                if hasattr(self, 'add_sample_button'):
                    self.add_sample_button.setEnabled(False)
                if hasattr(self, 'edit_sample_button'):
                    self.edit_sample_button.setEnabled(False)
                if hasattr(self, 'edit_patient_button'):
                    self.edit_patient_button.setEnabled(False)
                if hasattr(self, 'send_button'):
                    self.send_button.setEnabled(False)
                if hasattr(self, 'export_button'):
                    self.export_button.setEnabled(False)
                if hasattr(self, 'select_all_button'):
                    self.select_all_button.setEnabled(False)
                if hasattr(self, 'clear_selection_button'):
                    self.clear_selection_button.setEnabled(False)
            
            # 系统设置权限
            if role != UserRole.ADMIN:
                if hasattr(self, 'connect_button'):
                    self.connect_button.setEnabled(False)
                if hasattr(self, 'test_button'):
                    self.test_button.setEnabled(False)
                if hasattr(self, 'comm_type_combo'):
                    self.comm_type_combo.setEnabled(False)
                if hasattr(self, 'ip_edit'):
                    self.ip_edit.setEnabled(False)
                if hasattr(self, 'port_spin'):
                    self.port_spin.setEnabled(False)
                if hasattr(self, 'serial_port_combo'):
                    self.serial_port_combo.setEnabled(False)
                if hasattr(self, 'baudrate_combo'):
                    self.baudrate_combo.setEnabled(False)
                if hasattr(self, 'tcp_server_host_edit'):
                    self.tcp_server_host_edit.setEnabled(False)
                if hasattr(self, 'tcp_server_port_spin'):
                    self.tcp_server_port_spin.setEnabled(False)
                if hasattr(self, 'tcp_server_max_clients_spin'):
                    self.tcp_server_max_clients_spin.setEnabled(False)
            
            # 用户管理权限
            if role != UserRole.ADMIN:
                if hasattr(self, 'add_user_button'):
                    self.add_user_button.setEnabled(False)
                if hasattr(self, 'edit_user_button'):
                    self.edit_user_button.setEnabled(False)
                if hasattr(self, 'delete_user_button'):
                    self.delete_user_button.setEnabled(False)
                if hasattr(self, 'batch_delete_users_button'):
                    self.batch_delete_users_button.setEnabled(False)
                if hasattr(self, 'change_password_button'):
                    self.change_password_button.setEnabled(False)
                if hasattr(self, 'permission_button'):
                    self.permission_button.setEnabled(False)
            
            # 记录权限更新日志
            logger.info(f"已根据用户角色 {role.value} 更新UI权限")
        
        except Exception as e:
            logger.error(f"更新UI权限失败: {e}")
    
    def view_sample_details(self):
        """查看样本详情（查看员专用）"""
        try:
            # 获取选中的样本
            selected_items = self.sample_table.selectedItems()
            if not selected_items:
                QMessageBox.information(self, "提示", "请先选择要查看的样本")
                return
            
            # 获取样本ID（从第一列）
            sample_id = selected_items[0].text()
            
            # 从数据库获取样本详细信息
            sample = self.db_manager.get_sample_by_id(sample_id)
            if not sample:
                QMessageBox.warning(self, "警告", "未找到选中的样本")
                return
            
            # 显示样本详情对话框
            from src.ui.dialogs import SampleEditDialog
            dialog = SampleEditDialog(self, sample)
            dialog.exec()
            
        except Exception as e:
            logger.error(f"查看样本详情失败: {e}")
            QMessageBox.critical(self, "错误", f"查看样本详情失败: {e}")